home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1993 #2 / Image.iso / clipper / nettos11.zip / MHS / MHS.PRG
Text File  |  1993-04-17  |  19KB  |  573 lines

  1. /*
  2.  * File......: MHS.PRG
  3.  * Author....: Norbert Sommer
  4.  * CIS ID....: 100016,1241
  5.  * Date......: $Date$
  6.  * Revision..: $Revision$
  7.  * Log file..: $Logfile$
  8.  * 
  9.  * This is an original program by Norbert Sommer
  10.  * and is hereby placed in the public domain.
  11.  *
  12.  * Modification history:
  13.  * ---------------------
  14.  *
  15.  * $Log$
  16.  *
  17.  */
  18.  
  19. #include "fileio.ch"
  20. #include "ftint86.ch"
  21. #include "netto.ch"                              // with MHS extensions
  22.  
  23. #define MHS_HOST                 0               // Record types in netdir.tab
  24. #define MHS_USERS                2
  25. #define MHS_HOSTS_WGROUPS_GWAYS  4
  26. #define MHS_AFF_WGROUPS          7
  27. #define MHS_APPS                 9
  28.  
  29. #define CRLF        chr(13)+chr(10)
  30.  
  31. STATIC cMhsPath := ""                            // storing MHS path in variable
  32.                                                  // has to end with \
  33. STATIC aMonthAm := {"Jan", "Feb", "Mar", "Apr",; // has to be american
  34.                     "May", "Jun", "Jul", "Aug",;
  35.                     "Sep", "Oct", "Nov", "Dec" }
  36.  
  37. #ifdef FT_TEST
  38.   // ----------------------------------
  39.   FUNCTION mhs ( cTo, cFrom, cSubject, cMessage, cFile )
  40.   // ----------------------------------
  41.   LOCAL nErrorCode, cHost, aUser, aWGroups, aAffWGr, aApps
  42.  
  43.     DEFAULT cMessage TO "Testmessage fn_MhsSndM()" + CRLF + ;
  44.                         "send " + dtoc(date()) ,            ;
  45.             cSubject TO "Test fn_mhs "+time()
  46.     CLS
  47.  
  48.     IF pcount() < 2
  49.        ? "Minimum usage: MHS ToUser@Host1 Me@Host2"
  50.     ELSE
  51.        nErrorCode := FN_MHSSndM ( {cTo}, cFrom, cSubject, cMessage, {cFile} )
  52.        ? "Mail Return Code:" + str(nErrorCode,3)
  53.        inkey(0.5)
  54.  
  55.        IF nErrorcode == ESUCCESS
  56.           cHost   :=fn_MhsHost() ; qout("Host: " + cHost)
  57.           aUser   :=fn_MhsUser() ; qout("User: ")
  58.                                    aeval(aUser   ,{|e|qout(e)})
  59.           aWGroups:=fn_MhsHWG()  ; qout("Hosts, Workgroups and Gateways:")
  60.                                    aeval(aWGroups,{|e|qout(e)})
  61.           aAffWGr :=fn_MhsAfWG() ; qout("Affiliated Workgroups:")
  62.                                    aeval(aAffWgr ,{|e|qout(e)})
  63.           aApps   :=fn_MhsApps() ; qout("Registered applications:")
  64.                                    aeval(aApps   ,{|e|qout(e)})
  65.        ENDIF
  66.     ENDIF
  67.     ?
  68.   RETURN ( nil )
  69. #endif
  70.  
  71.  
  72. /*  $DOC$
  73.  *  $FUNCNAME$
  74.  *      Overview
  75.  *  $CATEGORY$
  76.  *      MHS
  77.  *  $ONELINER$
  78.  *      MHS Send Mail Services
  79.  *  $DESCRIPTION$
  80.  *      
  81.  *      These MHS Services provide functions for _sending_ email
  82.  *      messages with attached files from within your Clipper
  83.  *      application. For example, mails can be maintenance infos or automatic
  84.  *      report files.
  85.  *      Mails can be sent to multiple recipients and with multiple
  86.  *      attached files with one function call.
  87.  *      MHS tables can be read to find known users and hosts.
  88.  *
  89.  *      To use these functions "NetWare MHS" version 1.5 or higher or
  90.  *      "NetWare Global Messaging" (NGM) must be installed in your network
  91.  *      environment.
  92.  *      The reciever may use every version of MHS or NGM and needs an
  93.  *      email application such as daVinci EMail, daVinci Coordinator or
  94.  *      Pegasus Mail.
  95.  *
  96.  *      The DOS variable MV points to the MHS directory structure and has
  97.  *      been set properly if one of the above products is installed.
  98.  *      Personal or network versions of NetWare MHS are detected.
  99.  *
  100.  *      All the modem and routing stuff will be done by MHS! The only
  101.  *      thing we are really doing is writing our files with the right
  102.  *      structure into the MHS directories under %MV%\MHS.
  103.  *
  104.  *      Please inform your email administator that you are testing because
  105.  *      he will receive all undelivarable mail and error messages.
  106.  *      
  107.  *      Receiving MHS mail in an application is a much more complicated 
  108.  *      problem because the application 
  109.  *      needs!!! tto be registration with MHS and a lot
  110.  *      of error checking is necessary.
  111.  *      Program to program communcation can be done with semaphores,
  112.  *      IPX/SPX sockets or broadcasting.
  113.  *
  114.  *  $END$
  115.  */
  116.  
  117. /*  $DOC$
  118.  *  $FUNCNAME$
  119.  *     FN_MhsSndM()
  120.  *  $CATEGORY$
  121.  *     MHS
  122.  *  $ONELINER$
  123.  *     Send MHS Message to multiple recipients with attachments
  124.  *  $SYNTAX$
  125.  *     FN_MhsSndM( <aTo>, <cFrom>, <cSubject>, <cMessage> ;
  126.  *                 [,<aFiles>] [,<aCC>] [,<nSMFVer>] )    ;
  127.  *               => nErrorCode
  128.  *  $ARGUMENTS$
  129.  *     <aTo>      is the list of addressees containing complete 
  130.  *                addresses User[.App] @ Host|Workgroup|Gateway .
  131.  *                Max Length = 64 elements
  132.  *
  133.  *     <cFrom>    Sender of the message. Complete address necessary.
  134.  *
  135.  *     <cSubject> Short subject of the message.
  136.  *                Maximum Length 64 Bytes.
  137.  *
  138.  *     <cMessage> Long message to be sent. Max Length = 64 KB
  139.  *
  140.  *     <aFiles>   List of files to be sent as attachments to the
  141.  *                message. Give complete path. Max Length 64 ele-
  142.  *                ments. If nSMFVer is set to 64, only one attach-
  143.  *                ment is possible.
  144.  *
  145.  *     <aCC>      List of addressees to get a copy of the message.
  146.  *                Complete address necessary. Max Length = 63 ele-
  147.  *                ments. The sum of elements of aTo and aCC may
  148.  *                not exceed 64.
  149.  *
  150.  *     <nSMFVer>  is the SMF-Version of the message format.
  151.  *                Allowed are 64 (MHS 1.1), 70 (MHS 1.5) and
  152.  *                71 (MHS 2.0, NGM).
  153.  *                Default is 70, normally there is no need to change this.
  154.  *  $RETURNS$
  155.  *     <nErrorCode>
  156.  *                ESUCCESS        0   ok
  157.  *                EMHS_PARAM     -1   parameter error
  158.  *                EMHS_MV        -2   MV variable not found
  159.  *                EMHS_MAILSND   -3   directory mhs\mail\snd not found
  160.  *                EMHS_CREATE    -4   file creating error
  161.  *                EMHS_SEND      -5   error sending message
  162.  *                EMHS_LIST      -6   list too long
  163.  *
  164.  *  $DESCRIPTION$
  165.  *     Sends a message to multiple recipients using Novell MHS,
  166.  *     which must be installed. Files can be attached.
  167.  *
  168.  *  $EXAMPLES$
  169.  *     // This will send a message from user NSommer to
  170.  *        UZyka in the Hannover office, 2 files are attached:
  171.  *
  172.  *     nErrorCode:= FN_MHSSndM({"UZyka@dcs-ha"}, "NSommer@dcs-sg",  ;
  173.  *                        "Reports Oct and Nov", "MessageBody",     ;
  174.  *                          {"c:\rep\oct92.dbf", "c:\pict\nov92.pcx"} )
  175.  *
  176.  *     // if a mhs-fax gateway is installed with extended addressing:
  177.  *
  178.  *     nErrorcode:= FN_MHSSndM({"fax@fax {fax:299-399-499}"},       ;
  179.  *                         "NSommer@dcs-sg", "TestFax", cLongMessage )
  180.  *
  181.  *  $INCLUDE$
  182.  *      netto.CH
  183.  *  $SEEALSO$
  184.  *      fn_MhsHost() fn_MhsUser()
  185.  *  $END$
  186.  */
  187.  
  188. FUNCTION fn_MhsSndM(aTo, cFrom, cSubject, cMessage, aFiles, aCC, nSMFVer)
  189. LOCAL nErrorCode:=0, cMV:="", hMessFile, cHeader:="", ;
  190.       aSmfOk:={64,70,71}, aTmpFiles:={}
  191.  
  192. DEFAULT nSMFVer TO 70, cSubject TO "", cMessage TO ""
  193.  
  194. DO CASE
  195.    CASE empty(aTo)                  .or. ;
  196.         valtype(aTo) # "A"          .or. ;
  197.         len(aTo) > 64               .or. ;
  198.         empty(cFrom)                .or. ;
  199.         valtype(cFrom) # "C"        .or. ;
  200.         valtype(cSubject) # "C"     .or. ;
  201.         valtype(cMessage) # "C"     .or. ;
  202.         len(cMessage) > 65500       .or. ;
  203.         ascan(aSmfOk,nSMFVer) == 0
  204.  
  205.         nErrorCode:=EMHS_PARAM                             // Parameter ok ?
  206.  
  207.    CASE empty(cMV:=gete("MV"))                             // MV found ?
  208.         nErrorCode:=EMHS_MV
  209.  
  210.    CASE empty(cMhsPath:=_fnMhsPath(cMV))                   // Mail path found ?
  211.         nErrorCode:=EMHS_MAILSND
  212.  
  213.    CASE (hMessFile:=fopen(ft_TempFil(cMhsPath+"MHS\MAIL\SND"),2)) < 0
  214.         nErrorCode:=EMHS_CREATE                            // Unique File R/W
  215. ENDCASE
  216.  
  217. IF nErrorCode == 0                                         // Building message
  218.    cHeader := "SMF-"   + str(nSMFVer,2) + CRLF + ;         // SMF Version
  219.               "To: "   + aTo[1]
  220.    aeval(aTo, {|to| cHeader+=", "+to} , 2, 64)             // To
  221.    cHeader += CRLF + ;
  222.               "From: " + cFrom + CRLF                      // From
  223.  
  224.    IF ! empty(aCC)
  225.       cHeader += "Copies-to: " + aCC[1]                    // CC
  226.       aeval(aCC, {|cc| cHeader+=","+cc} , 2, 64)
  227.       cHeader+= CRLF
  228.    ENDIF
  229.  
  230.    cHeader += "Date: " + str(day(date()),2)      + "-" + ; // Date
  231.                          aMonthAm[month(date())] + "-" + ;
  232.                          right(str(year(date())),2) + " " + ;
  233.                          time() + CRLF
  234.    IF !empty(cSubject)
  235.       cHeader += "Subject: " + cSubject + CRLF             // Subject
  236.    ENDIF
  237.                                                            // Files attached
  238.    IF ! empty( aFiles ) .and. ;
  239.       ! empty( aTmpFiles := _fnMHSTmAt(@aFiles) )
  240.       IF nSMFVer < 70 .and. len(aFiles) > 1                // SMF-64 only one
  241.          asize(aFiles,1) ; asize (aTmpFiles,1)             //   attachment
  242.          nErrorCode:=EMHS_LIST
  243.       ENDIF
  244.       cHeader += "Attachment: " + aTmpFiles[1]
  245.       aeval(aTmpFiles, { |Tmpfile| cHeader += ", " + TmpFile }, 2, 64)
  246.       cHeader += CRLF+ "Attachment-name: " + aFiles[1]
  247.       aeval(aFiles, { |file| cHeader += ", "+ file }, 2, 64)
  248.       cHeader += CRLF
  249.    ENDIF
  250.  
  251.    cHeader += CRLF                                         // empty line required
  252.    IF fwrite(hMessFile, cHeader)  < len(cHeader) .or. ;    // Write header
  253.       fwrite(hMessFile, cMessage) < len(cMessage)          // Write Message
  254.       nErrorCode:=EMHS_SEND
  255.    ENDIF
  256.    fwrite(hMessFile, CRLF)
  257.    fclose(hMessFile)                                       // Closing file
  258. ENDIF
  259.  
  260. RETURN (nErrorCode)
  261.  
  262.  
  263. *--------------------------------------
  264. STATIC FUNCTION _fnMHSTmAt(aFiles)     // copies files using unique names
  265.                                        // into %mhs%\mail\parcel and
  266.                                        // returns an array of the copied files!!!
  267.                                        // filesnames
  268.                                        // => aTmpFiles
  269. *--------------------------------------
  270. LOCAL aTmpFiles:={}, i, tmpFile:="", nDel:=0, nPosBsp:=0, nPosDp:=0
  271.  
  272. asize(aTmpFiles,len(aFiles))
  273.  
  274. FOR i:=len(aFiles) TO 1 STEP -1
  275.     IF file ( aFiles[i] ) .and. ;
  276.        !empty ( tmpFile:=ft_TempFil(cMhsPath+"MHS\MAIL\PARCEL") )
  277.        COPY FILE ( aFiles[i] ) TO ( tmpFile )
  278.        aTmpFiles[i] := substr(tmpFile, RAT("\",TmpFile)+1)
  279.        IF ( nPosBsp := RAT("\",aFiles[i]) ) > 0 .or. ;     // extract filename
  280.           ( nPosDp  := RAT(":",aFiles[i]) ) > 0
  281.           aFiles[i] := substr(aFiles[i],max(nPosBsp,nPosDp)+1)
  282.        ENDIF
  283.     ELSE
  284.        adel(aFiles,i); adel(aTmpFiles,i); nDel ++
  285.     ENDIF
  286. NEXT i
  287.  
  288. asize(aFiles   ,len(afiles)-nDel)
  289. asize(aTmpFiles,len(aTmpFiles)-nDel)
  290.  
  291. RETURN (aTmpFiles)
  292.  
  293.  
  294.  
  295. /*  $DOC$
  296.  *  $FUNCNAME$
  297.  *     FN_MHSUSER()
  298.  *  $CATEGORY$
  299.  *     MHS
  300.  *  $ONELINER$
  301.  *     Get MHS user list
  302.  *  $SYNTAX$
  303.  *     FN_MhsUser() => aUser
  304.  *  $ARGUMENTS$
  305.  *     None
  306.  *  $RETURNS$
  307.  *     <aUser> Array of known local users at your MHS location
  308.  *  $DESCRIPTION$
  309.  *     Reading the MHS table file gives information on known MHS users
  310.  *     at your local MHS host.
  311.  *
  312.  *     If the MHS DOS variable USR is found, the mail id of the
  313.  *     currectly logged user can be read by gete("USR").
  314.  *
  315.  *     !!!Can be used to check input before sending a message to a local user
  316.  *     using fn_MhsSndM().
  317.  *
  318.  *  $EXAMPLES$
  319.  *     fnMhsUser() => {"GSCOTT","CYELLICK","ADMIN","CBROWN"}
  320.  *
  321.  *  $INCLUDE$
  322.  *
  323.  *  $SEEALSO$
  324.  *      FN_MhsHost() FN_MhsHWG() FN_MhsAfWG() FN_MhsApps()
  325.  *  $END$
  326.  */
  327. FUNCTION fn_MhsUser()
  328. RETURN   _fnMhsTab(MHS_USERS)
  329.  
  330.  
  331. /*  $DOC$
  332.  *  $FUNCNAME$
  333.  *     FN_MHSHWG()
  334.  *  $CATEGORY$
  335.  *     MHS
  336.  *  $ONELINER$
  337.  *     Get MHS table file of hosts, workgroups, and gateways
  338.  *  $SYNTAX$
  339.  *     FN_MhsHWG() => aHoWoGw
  340.  *  $ARGUMENTS$
  341.  *     None
  342.  *  $RETURNS$
  343.  *     <aHoWoGw> Array
  344.  *  $DESCRIPTION$
  345.  *     Reading the MHS table file gives information on known
  346.  *     hosts, workgroups and gateways.
  347.  *
  348.  *     They are used in the same way ( MHS knows what to do):
  349.  *     user @ host, user @ workgroup, user @ gateway .
  350.  *
  351.  *     Can be used to check input before sending a message to a local user
  352.  *     using fn_MhsSndM().
  353.  *
  354.  *  $EXAMPLES$
  355.  *      cToHost := "CSERVE"
  356.  *      if ascan( fn_MhsHWG(), cToHost ) > 0
  357.  *         fn_MhsSndM({"BMargos@"+cToHost}, "NSommer@dcs-sg", cMessage)
  358.  *      else
  359.  *         ? "Host not found"
  360.  *      endif
  361.  *
  362.  *  $INCLUDE$
  363.  *
  364.  *  $SEEALSO$
  365.  *      FN_MhsHost() FN_MhsUser() FN_MhsAfWG() FN_MhsApps()
  366.  *  $END$
  367.  */
  368. FUNCTION fn_MhsHWG()
  369. RETURN ( _fnMhsTab(MHS_HOSTS_WGROUPS_GWAYS) )
  370.  
  371.  
  372.  
  373. /*  $DOC$
  374.  *  $FUNCNAME$
  375.  *     FN_MHSAFWG()
  376.  *  $CATEGORY$
  377.  *     MHS
  378.  *  $ONELINER$
  379.  *     Get MHS list of affilitated workgroups
  380.  *  $SYNTAX$
  381.  *     FN_MhsAfWG() => aAffWGr
  382.  *  $ARGUMENTS$
  383.  *     None
  384.  *  $RETURNS$
  385.  *     <aUser> Array of affiliated workgroups at your MHS location
  386.  *  $DESCRIPTION$
  387.  *     Reading the MHS table file gives information on registered
  388.  *     affiliated workgroups at your local MHS host.
  389.  *
  390.  *     Only interesting for people who know about the  workgroup
  391.  *     concept in Novell's MHS.
  392.  *
  393.  *  $EXAMPLES$
  394.  *
  395.  *  $INCLUDE$
  396.  *
  397.  *  $SEEALSO$
  398.  *      FN_MhsHost() FN_MhsUser() FN_MhsHWG() FN_MhsApps()
  399.  *  $END$
  400.  */
  401. FUNCTION fn_MhsAfWG()
  402. RETURN ( _fnMhsTab(MHS_AFF_WGROUPS) )
  403.  
  404.  
  405. /*  $DOC$
  406.  *  $FUNCNAME$
  407.  *     FN_MHSAPPS()
  408.  *  $CATEGORY$
  409.  *     MHS
  410.  *  $ONELINER$
  411.  *     Get MHS list of registered applications
  412.  *  $SYNTAX$
  413.  *     FN_MhsApps() => aApps
  414.  *  $ARGUMENTS$
  415.  *     None
  416.  *  $RETURNS$
  417.  *     <aUser> Array of registered applications at your MHS location
  418.  *  $DESCRIPTION$
  419.  *     Generally one Email user can be reached in different mail
  420.  *     applications. MHS knows the preferred application,
  421.  *     but the receiving application can be predetermined.
  422.  *
  423.  *     fn_MhsApps() gives an array on locally known mail
  424.  *     applications at your MHS host.
  425.  *
  426.  *  $EXAMPLES$
  427.  *
  428.  *      cMyHost := "@" + fn_MhsHost()
  429.  *      if ascan( fn_MhsApps(), cApp ) > 0
  430.  *         fn_MhsSndM({"BMargos"+cMyHost,"CDragoi.ATC"+cMyHost},;
  431.  *                    "NSommer@dcs-sg", cMessage)
  432.  *      else
  433.  *         ? "App. not found"
  434.  *      endif
  435.  *
  436.  *      BMargos will receive the message at his preferred mail
  437.  *      application, but CDragoi will receive it using Action
  438.  *      Technologies Coordinator (ATC).
  439.  *
  440.  *      Registering applications and managing MHS is not
  441.  *      handled by this function and should be done by the 
  442.  *      network's MHS administrator.
  443.  *
  444.  *  $INCLUDE$
  445.  *
  446.  *  $SEEALSO$
  447.  *      FN_MhsHost() FN_MhsUser() FN_MhsHWG() FN_MhsAfWG()
  448.  *  $END$
  449.  */
  450. FUNCTION fn_MhsApps()
  451. RETURN ( _fnMhsTab(MHS_APPS) )
  452.  
  453.  
  454.  
  455. /*  $DOC$
  456.  *  $FUNCNAME$
  457.  *     FN_MhsHost()
  458.  *  $CATEGORY$
  459.  *     MHS
  460.  *  $ONELINER$
  461.  *     Get name of your MHS host
  462.  *  $SYNTAX$
  463.  *     FN_MhsHost() => cHost
  464.  *  $ARGUMENTS$
  465.  *     none
  466.  *  $RETURNS$
  467.  *     <cHost> Name of Host
  468.  *  $DESCRIPTION$
  469.  *     Reads the name of the MHS Host to which the MV variable
  470.  *     is pointing.
  471.  *
  472.  *  $EXAMPLES$
  473.  *     FN_MhsHost() => "NHUB"
  474.  *
  475.  *     Building the sender address:
  476.  *     gete("USR") + "@" + fn_MhsHost() or
  477.  *     fn_whoami() + "@" + fn_MhsHost()
  478.  *
  479.  *  $SEEALSO$
  480.  *      FN_MhsSndM() FN_MhsUser() FN_MhsApps() FN_MhsHWG()
  481.  *  $END$
  482.  */
  483. FUNCTION fn_MhsHost()
  484. LOCAL aHost:=_fnMhsTab(MHS_HOST), cHost:=""
  485.  
  486. IF !empty(aHost)
  487.    cHost :=substr(aHost[1],2,8)
  488. ENDIF
  489.  
  490. RETURN(cHost)
  491.  
  492.  
  493. *--------------------------------------
  494. FUNCTION _fnMhsTab(nRecType)
  495. *--------------------------------------
  496. LOCAL  hTabHndl, buffer:=space(128), aReturn:={}, nType
  497.  
  498. IF !empty(cMhsPath:=_fnMhsPath(gete("MV")) ) .AND. ;
  499.    (hTabHndl:=fopen(cMhsPath+"MHS\MAIL\PUBLIC\NETDIR.TAB",FO_READ) ) > 0
  500.                                                       // File sorted by nType
  501.  
  502.    DO WHILE fread(hTabHndl,@buffer,128) > 0 .and. ;
  503.                    (nType:=asc(buffer)) <= nRecType
  504.       IF nType == nRecType
  505.          aadd(aReturn, upper(substr(buffer,2,9)) )    // last char may be chr(0)
  506.       ENDIF
  507.    ENDDO
  508.    fclose(hTabHndl)
  509. ENDIF
  510.  
  511. RETURN(aReturn)
  512.  
  513.  
  514. *--------------------------------------
  515. STATIC FUNCTION _fnMhsPath(cMV)  // find MHS directory structure
  516.                                  // and give drive
  517.                                  // personal MHS: MV=d:\
  518.                                  // network  MHS: MV=[server/]vol:[dir]
  519.                                  // Directory \SND exists in MHS 1.5 and above
  520.                                  // Directory Handle will be cleared by
  521.                                  // implicit fn_eoj() at end of application
  522. *--------------------------------------
  523. LOCAL  cDrive:=" ", cPath:="", hMhsSnd, cSndDir:="MHS\MAIL\SND", ;
  524.        nPosSlash, nPosDp, cServer, cVolumeDir, nTmpDrive
  525.  
  526. DEFAULT cMV TO ""
  527.  
  528. IF right(cMV:=upper(cMV),1)#"\"
  529.    cMV += "\"
  530. ENDIF
  531.  
  532. DO CASE
  533.    CASE !empty(cMhsPath)               // has been found before and stored in static
  534.         cPath := cMhsPath              // nothing to do, give it back
  535.  
  536.    CASE substr(cMV,2,2) == ":\"        // MHS Personal  d:\[dir]
  537.         IF file(cMV + cSndDir +"\NUL") // works on local directory
  538.            cPath := cMV
  539.         ENDIF
  540.  
  541.    OTHERWISE                                          // network
  542.                                                       // find free temp drive
  543.         if (nTmpDrive:=ascan(fn_gDrvFT(),0,27)) > 0   // 27..32 are tmp drives
  544.            cDrive := chr(64+nTmpDrive)
  545.         else
  546.            cDrive := "]"                              // catch a used one
  547.         endif                                         // later: give error
  548.  
  549.         nPosSlash  := ft_at2(iif("/"$cMV,"/","\"), cMV, 1)
  550.         nPosDp     := ft_at2(":", cMV, 1)
  551.  
  552.         IF nPosSlash < nPosDp                              // server/vol:[dir]
  553.            cServer    := left  (cMV, nPosSlash-1)
  554.            cVolumeDir := substr(cMV, nPosSlash+1)
  555.            fn_sPfCID ( ASCAN( fn_FSName(), cServer ) )     // set pref. server
  556.         ELSE                                               // vol:[dir]
  557.            cVolumeDir := cMV
  558.         ENDIF
  559.  
  560.         hMhsSnd  := fn_AlTemDH(0, cDrive, cVolumeDir + cSndDir)
  561.         IF !empty(hMhsSnd) .and. !empty(hMhsSnd[1])
  562.            cPath := cDrive + substr(cMV, nPosDp)
  563.         ENDIF
  564. ENDCASE
  565.  
  566. cMhsPath := cPath
  567. RETURN (cPath)
  568.  
  569.  
  570.  
  571. *---- EOF MHS.PRG Nanforum Network Toolkit ----*
  572.  
  573.