home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Utilities / Ph 1.1.1 / PhClient / serv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-30  |  65.8 KB  |  2,363 lines  |  [TEXT/MPS ]

  1. /*_____________________________________________________________________
  2.  
  3.       serv.c - PH Server Interface.
  4. _____________________________________________________________________*/
  5.  
  6. #pragma load "precompile"
  7. #include "rez.h"
  8. #include "mtcp.h"
  9. #include "serv.h"
  10. #include "utl.h"
  11. #include "encrypt.h"
  12. #include "glob.h"
  13. #include "tran.h"
  14. #include "query.h"
  15. #include "help.h"
  16.  
  17. #pragma segment serv
  18.  
  19. /*_____________________________________________________________________
  20.  
  21.     Constants.
  22. _____________________________________________________________________*/
  23.  
  24. #define initHandleSize    1000            /* initial command and response buffer size */
  25. #define cmdHandleInc        100            /* command buffer size increment */
  26. #define updateInterval    (24*60*60)    /* one day = help and site list update interval */
  27.  
  28. /*_____________________________________________________________________
  29.  
  30.     Global Variables.
  31. _____________________________________________________________________*/
  32.  
  33. static Handle        Command;                        /* handle to server command */
  34. static Handle        Response;                    /* handle to server response */
  35. static Boolean        Truncated;                    /* true if server response truncated */
  36. static Str255        ReadOnlyReason="\p";        /* reason server is read-only */
  37.  
  38. /*_____________________________________________________________________
  39.  
  40.     OpenConnection - Open Connection to Server.
  41.     
  42.     Entry:    server = Ph server host domain name.
  43. _____________________________________________________________________*/
  44.  
  45. static OSErr OpenConnection (Str255 server)
  46.  
  47. {
  48.     Command = NewHandle(initHandleSize);
  49.     if (!Command) return MemError();
  50.     Response = NewHandle(initHandleSize);
  51.     if (!Response) return MemError();
  52.     return mtcp_OpenConnection(server);
  53. }
  54.  
  55. /*_____________________________________________________________________
  56.  
  57.     CloseConnection - Close Connection to Server.
  58. _____________________________________________________________________*/
  59.  
  60. static OSErr CloseConnection (void)
  61.  
  62. {
  63.     if (Command) DisposHandle(Command);
  64.     if (Response) DisposHandle(Response);
  65.     return mtcp_CloseConnection();
  66. }
  67.  
  68. /*_____________________________________________________________________
  69.  
  70.     AbortConnection - Abort Connection to Server.
  71. _____________________________________________________________________*/
  72.  
  73. static OSErr AbortConnection (void)
  74.  
  75. {
  76.     if (Command) DisposHandle(Command);
  77.     if (Response) DisposHandle(Response);
  78.     return mtcp_AbortConnection();
  79. }
  80.  
  81. /*_____________________________________________________________________
  82.  
  83.     DoCommand - Issue One Command to Server.
  84.     
  85.     Entry:    cmd = command.
  86.     
  87.     Exit:        Response = response.
  88. _____________________________________________________________________*/
  89.  
  90. static OSErr DoCommand (Str255 cmd)
  91.  
  92. {
  93.     short                cmdLen;            /* command length */
  94.  
  95.     cmdLen = *cmd;
  96.     memcpy(*Command, cmd+1, cmdLen);
  97.     *(*Command + cmdLen) = '\r';
  98.     return mtcp_PhCommand(Command, Response, &Truncated);
  99. }
  100.  
  101. /*_____________________________________________________________________
  102.  
  103.     Skip - Skip to Next Response Field
  104.                 
  105.     Entry:        *p = pointer into response.
  106.     
  107.     Exit:            function result = true if error (no next field).
  108.                     *p = pointer to first char of next field.
  109. _____________________________________________________________________*/
  110.  
  111. static Boolean Skip (char **p)
  112.  
  113. {
  114.     char *q;
  115.  
  116.     q = strpbrk(*p, ":\r");
  117.     if (!q || *q == '\r') return true;
  118.     q++;
  119.     q += strspn(q, " \t");
  120.     if (*q == '\r') return true;
  121.     *p = q;
  122.     return false;
  123. }
  124.  
  125. /*_____________________________________________________________________
  126.  
  127.     GetServErrMsg - Get Server Error Message.
  128.     
  129.     Entry:    p = pointer to beginning of error message line in response.
  130.     
  131.     Exit:        servErrMsg = server error message.
  132. _____________________________________________________________________*/
  133.  
  134. static void GetServErrMsg (char *p, Str255 servErrMsg)
  135.  
  136. {
  137.     char            *q;            /* pointer into response line */
  138.     short            len;            /* message length */
  139.     short            sCode;        /* server error code */
  140.  
  141.     sCode = atoi(p);
  142.     *servErrMsg = 0;
  143.     while (true) {
  144.         p = strchr(p, ':') + 1;
  145.         q = strchr(p, '\r');
  146.         len = q-p;
  147.         if (*servErrMsg + len > 255) len = 255 - *servErrMsg;
  148.         memcpy(servErrMsg+*servErrMsg+1, p, len);
  149.         *servErrMsg += len;
  150.         p = q+1;
  151.         if (atoi(p) != sCode) break;
  152.         if (*servErrMsg < 255) {
  153.             (*servErrMsg)++;
  154.             *(servErrMsg+*servErrMsg) = ' ';
  155.         }
  156.     }
  157. }
  158.  
  159. /*_____________________________________________________________________
  160.  
  161.     GetFieldInfo - Get Ph Server Field Info.
  162.                 
  163.     Exit:        function result = error code.
  164.                 fields = handle to allocated and initialized field info array.
  165.                 numFields = number of fields.
  166.                 sCode = server response code:
  167.                     phSuccess = success.
  168.                     other = unexpected.
  169.                 
  170. fields
  171. -200:6:alias:max 32 Indexed Lookup Public Default Change
  172. -200:6:alias:Unique name for user, chosen by user.
  173. -200:3:name:max 256 Indexed Lookup Public Default
  174. -200:3:name:Full name.
  175. ...
  176. -200:25:all:max 1
  177. -200:25:all:reserved
  178. ...
  179. -200:34:text:max 4095 Lookup Public Default
  180. -200:34:text:Miscellaneous text
  181. 200:Ok.
  182. _____________________________________________________________________*/
  183.  
  184. static OSErr GetFieldInfo (FieldInfo ***fields, short *numFields, short *sCode)
  185.  
  186. {
  187.     Handle            h;                        /* handle to field info array */
  188.     OSErr                rCode;                /* result code */
  189.     Str255            cmd;                    /* server command */
  190.     char                *p;                    /* pointer into response */
  191.     char                *q;                    /* pointer into response */
  192.     FieldInfo        *f;                    /* pointer to field info record */
  193.     short                len;                    /* string length */
  194.     short                code;                    /* server response code */
  195.  
  196.     *numFields = 0;
  197.     h = NewHandle(0);
  198.     if (!h) return MemError();
  199.     GetIndString(cmd, serverCmds, phFields);
  200.     if (rCode = DoCommand(cmd)) return rCode;
  201.     HLock(Response);
  202.     p = *Response+1;
  203.     while (true) {
  204.         code = atoi(p);
  205.         if (code != -phSuccess) break;
  206.         HUnlock(h);
  207.         SetHandleSize(h, GetHandleSize(h) + sizeof(FieldInfo));
  208.         HLock(h);
  209.         f = (FieldInfo*)(*h + *numFields*sizeof(FieldInfo));
  210.         code = phSyntax;
  211.         if (Skip(&p)) break;
  212.         if (Skip(&p)) break;
  213.         q = strpbrk(p, ":\r");
  214.         if (!q || *q == '\r') break;
  215.         len = q-p;
  216.         if (len > maxFieldName) break;
  217.         memcpy(f->name+1, p, len);
  218.         *f->name = len;
  219.         *(f->name+len+1) = 0;
  220.         p = q+1;
  221.         p = strpbrk(p, "0123456789\r");
  222.         if (!p || *p == '\r') break;
  223.         f->maxSize = atoi(p);
  224.         if (f->maxSize <= 0) break;
  225.         p = strpbrk(p, " \r");
  226.         if (!p) break;
  227.         if (*p == ' ') {
  228.             p++;
  229.             q = strchr(p, '\r');
  230.             if (!q) break;
  231.             len = q-p;
  232.             if (len > 254) break;
  233.             f->attributes = NewHandle(len+2);
  234.             memcpy(*f->attributes+1, p, len);
  235.             **f->attributes = len;
  236.             *(*f->attributes+len+1) = 0;
  237.             p = q+1;
  238.         } else {
  239.             p++;
  240.             f->attributes = NewHandle(2);
  241.             **f->attributes = *(*f->attributes+1) = 0;
  242.         }
  243.         code = atoi(p);
  244.         if (code == phSuccess) {
  245.             code = phSyntax;
  246.             break;
  247.         }
  248.         if (code != -phSuccess) break;
  249.         code = phSyntax;
  250.         if (Skip(&p)) break;
  251.         if (Skip(&p)) break;
  252.         if (Skip(&p)) break;
  253.         q = strchr(p, '\r');
  254.         if (!q) break;
  255.         len = q-p;
  256.         if (len > 254) break;
  257.         f->description = NewHandle(len+2);
  258.         memcpy(*f->description+1, p, len);
  259.         **f->description = len;
  260.         *(*f->description+len+1) = 0;
  261.         p = q+1;
  262.         f->original = NewHandle(0);
  263.         f->origSize = 0;
  264.         (*numFields)++;
  265.     }
  266.     HUnlock(Response);
  267.     HUnlock(h);
  268.     *sCode = abs(code);
  269.     if (code == phSuccess) {
  270.         *fields = (FieldInfo**)h;
  271.     } else {
  272.         DisposHandle(h);
  273.     }
  274.     return noErr;
  275. }
  276.  
  277. /*_____________________________________________________________________
  278.  
  279.     GetServerStatus - Get Server Status.
  280.                 
  281.     Exit:        function result = error code.
  282.                 ReadOnlyReason = read-only reason.
  283.                 
  284. status
  285. 200:Database ready.
  286.  
  287. status
  288. 100:The database is now read-only (for database update).
  289. 201:Database ready, read only (for database update).
  290.  
  291. status
  292. 201:Database ready, read only (for database update).
  293. _____________________________________________________________________*/
  294.  
  295. OSErr GetServerStatus (void)
  296.  
  297. {
  298.     OSErr                rCode;                /* result code */
  299.     Str255            cmd;                    /* server command */
  300.     char                *p;                    /* pointer into response */
  301.     char                *q;                    /* pointer into response */
  302.     short                len;                    /* string length */
  303.     short                code;                    /* server response code */
  304.     
  305.     GetIndString(cmd, serverCmds, phStatus);
  306.     if (rCode = DoCommand(cmd)) return rCode;
  307.     HLock(Response);
  308.     p = *Response+1;
  309.     code = atoi(p);
  310.     *ReadOnlyReason = 0;
  311.     while (true) {
  312.         if (code == phSuccess) {
  313.             break;
  314.         } else if (code == phReadOnly) {
  315.             p = strpbrk(p, "(\r");
  316.             if (!p || *p == '\r') break;
  317.             p++;
  318.             q = strpbrk(p, ")\r");
  319.             if (!q || *q == '\r') break;
  320.             len = q-p;
  321.             if (len > 255) len = 255;
  322.             *ReadOnlyReason = len;
  323.             memcpy(ReadOnlyReason+1, p, len);
  324.             break;
  325.         } else if (code == phInProgress) {
  326.             p = strchr(p, '\r');
  327.             if (!p) break;
  328.             p++;
  329.         }
  330.     }
  331.     HUnlock(Response);
  332.     return noErr;
  333. }
  334.  
  335. /*_____________________________________________________________________
  336.  
  337.     GetAlias - Get Alias for Username.
  338.     
  339.     Entry:    user = username.
  340.                 
  341.     Exit:        function result = error code.
  342.                 alias = alias.
  343.                 sCode = server response code:
  344.                     phSuccess = success.
  345.                     phNoMatches = no matches to username.
  346.                     phManyMatches = more than one match to username.
  347.                     phFieldNotThere = record has no alias.
  348.                     other = unexpected.
  349.  
  350. query norstad return alias
  351. 102:There was 1 match to your request.
  352. -200:1:   alias: j-norstad
  353. 200:Ok
  354. .
  355. query john return alias
  356. 102:There were 3 matches to your request.
  357. -200:1:   alias: j-norstad
  358. -200:2:   alias: J-Franks
  359. -200:3:   alias: j-smith
  360. 200:Ok.
  361.  
  362. query susan return alias
  363. 501:No matches to your query.
  364.  
  365. query john return alias
  366. 502:Too many entries to print.
  367.  
  368. query noalias return alias
  369. 102:There was 1 match to your request.
  370. -508:1:   alias: Not present in entry.
  371. 200:Ok.
  372. _____________________________________________________________________*/
  373.  
  374. static OSErr GetAlias (Str255 user, Str255 alias, short *sCode)
  375.  
  376. {
  377.     OSErr            rCode;                /* result code */
  378.     Str255        tmpl;                    /* server command template */
  379.     Str255        cmd;                    /* server command */
  380.     char            *p;                    /* pointer into response */
  381.     char            *q;                    /* pointer into response */
  382.     short            nAlias;                /* number of aliases for this name */
  383.     short            len;                    /* string length */
  384.     short            code;                    /* server response code */
  385.         
  386.     GetIndString(tmpl, serverCmds, phQueryAlias);
  387.     utl_PlugParams(tmpl, cmd, user, nil, nil, nil);
  388.     if (rCode = DoCommand(cmd)) return rCode;
  389.     nAlias = 0;
  390.     HLock(Response);
  391.     p = *Response+1;
  392.     while (true) {
  393.         code = atoi(p);
  394.         if (code == -phSuccess) {
  395.             nAlias++;
  396.             if (nAlias > 1) break;
  397.             code = phSyntax;
  398.             if (Skip(&p)) break;
  399.             if (Skip(&p)) break;
  400.             if (Skip(&p)) break;
  401.             q = strchr(p, '\r');
  402.             if (!q) break;
  403.             len = q-p;
  404.             if (len > 255) break;
  405.             memcpy(alias+1, p, len);
  406.             *alias = len;
  407.         } else if (code == phMatchCount) {
  408.             q = strchr(p, '\r');
  409.             code = phSyntax;
  410.             if (!q) break;
  411.         } else {
  412.             break;
  413.         }
  414.         p = q+1;
  415.     }
  416.     HUnlock(Response);
  417.     code = abs(code);
  418.     if (code == phSuccess && nAlias != 1) {
  419.         if (!nAlias) {
  420.             *sCode = phNoMatches;
  421.         } else if (nAlias > 1) {
  422.             *sCode = phManyMatches;
  423.         }
  424.     } else {
  425.         *sCode = code;
  426.     }
  427.     return noErr;
  428. }
  429.  
  430. /*_____________________________________________________________________
  431.  
  432.     Login - Login to Server.
  433.     
  434.     Entry:    user = alias or usernmae.
  435.                 pswd = password.
  436.                 
  437.     Exit:        function result = error code.
  438.                 alias = alias.
  439.                 sCode = server response code:
  440.                     phSuccess = successful login.
  441.                     phPermErr = login failed due to bad username or password.
  442.                     phManyMatches = more than one match to username.
  443.                     phFailReadOnly = database is read-only.
  444.                     phFieldNotThere = record has no alias.
  445.                     other = unexpected.
  446.                 
  447. login "j-blow"
  448. 301:O;T`)4A&%,B;[4.\4C;14"YI";@20)B_D5^LH>QLI2
  449. answer $%dkte&65k8(
  450. 200:j-blow:Hi how are you?
  451.  
  452. login "j-blow"
  453. 301:;4">_G0C&6[$[$_KA5`2_VR=#0K@XE,2X-OWS_9X44
  454. answer #$%^&
  455. 500:Login failed
  456.  
  457. login j-norstad
  458. 301:A9E)*[=FYTAWCS5"?2NNHZMUB.EY]B,=ZQF$K"ICU)
  459. clear x
  460. 517:login not allowed to read-only database.
  461. status
  462. 201:Database ready, read only (for database update).
  463. _____________________________________________________________________*/
  464.  
  465. static OSErr Login (Str255 user, Str255 pswd, Str255 alias, short *sCode)
  466.  
  467. {
  468.     OSErr            rCode;                /* result code */
  469.     Str255        tmpl;                    /* server command template */
  470.     Str255        cmd;                    /* server command */
  471.     Boolean        haveTriedAlias;    /* true after alias login attempt failed */
  472.     char            *p;                    /* pointer into response */
  473.     char            *q;                    /* pointer into response */
  474.     short            encryptLen;            /* length of encrypted string */
  475.     short            code;                    /* server response code */
  476.     
  477.     utl_CopyPString(alias, user);
  478.     haveTriedAlias = false;
  479.     while (true) {
  480.         GetIndString(tmpl, serverCmds, phLogin);
  481.         utl_PlugParams(tmpl, cmd, alias, nil, nil, nil);
  482.         if (rCode = DoCommand(cmd)) return rCode;
  483.         code = atoi(*Response+1);
  484.         if (code == phFailReadOnly || code == phFailReadOnly2) {
  485.             code = phFailReadOnly;
  486.             GetServerStatus();
  487.             *sCode = code;
  488.             return noErr;
  489.         }
  490.         if (code != phEncrypt) break;
  491.         HLock(Response);
  492.         p = *Response+1;
  493.         code = phSyntax;
  494.         if (Skip(&p)) break;
  495.         q = strchr(p, '\r');
  496.         if (!q) break;
  497.         *q = 0;
  498.         p2cstr(pswd);
  499.         crypt_start(pswd);
  500.         c2pstr(pswd);
  501.         GetIndString(cmd, serverCmds, phAnswer);
  502.         encryptLen = encryptit(cmd + *cmd + 1, p);
  503.         HUnlock(Response);
  504.         *cmd += encryptLen;
  505.         if (rCode = DoCommand(cmd)) return rCode;
  506.         code = atoi(*Response+1);
  507.         if (code == phPermErr) {
  508.             if (haveTriedAlias) break;
  509.         } else if (code == phFailReadOnly || code == phFailReadOnly2) {
  510.             code = phFailReadOnly;
  511.             GetServerStatus();
  512.             *sCode = code;
  513.             return noErr;
  514.         } else {
  515.             break;
  516.         }
  517.         if (rCode = GetAlias(user, alias, &code)) return rCode;
  518.         if (code != phSuccess) break;
  519.         haveTriedAlias = true;
  520.     }
  521.     HUnlock(Response);
  522.     if (code == phNoMatches) code = phPermErr;
  523.     *sCode = abs(code);
  524.     return noErr;
  525. }
  526.  
  527. /*_____________________________________________________________________
  528.  
  529.     GetFieldData - Get Field Data from Server.
  530.     
  531.     Entry:    user = alias or name.
  532.                 fields = handle to field info array.
  533.                 numfields = number of fields.
  534.                 
  535.     Exit:        function result = error code.
  536.                 field data set in original and origSize fields.
  537.                 sCode = server response code:
  538.                     phSuccess = success.
  539.                     phNoMatches = no matches to username.
  540.                     phManyMatches = more than one match to username.
  541.                     other = unexpected.
  542.                     
  543. query alias="j-norstad" return all
  544. 102:There was 1 match to your request.
  545. -200:1:             alias: j-norstad
  546. -200:1:              name: John Norstad
  547. -200:1:             email: jln@casbah.acns.nwu.edu
  548. -200:1:             phone: (708)491-4077
  549. -200:1:               fax: (708)491-3824
  550. -200:1:           address: Academic Computing and Network Services
  551. -200:1:                  : 2129 Sheridan Road
  552. -200:1:                  : Evanston, IL 60208
  553. -200:1:   office_location: Vogelback Room 125
  554. -200:1:              type: Staff
  555. -522:1:          password: Encrypted; cannot be viewed.
  556. -200:1:          nickname: jln
  557. -200:1:              hero: yes
  558. -200:1:        department: ACNS, Networking & Communications
  559. -200:1:             title: Network Analyst
  560. -200:1:             hours: My hours vary widely.  I usually work days, but not always.
  561. -200:1:                  : Your best bet is 10-4 M-F.
  562. -200:1:             other: Mac networking, programming, and virus guru.
  563. -200:1:                  : Author of the "Disinfectant" Mac anti-viral utility.
  564. 200:Ok.
  565.  
  566. query alias="bogus" return all
  567. 501:No matches to your query.
  568. _____________________________________________________________________*/
  569.  
  570. static OSErr GetFieldData (Str255 user, FieldInfo **fields, short numFields, 
  571.     short *sCode)
  572.  
  573. {
  574.     OSErr            rCode;                /* result code */
  575.     Str255        tmpl;                    /* server command template */
  576.     Str255        cmd;                    /* server command */
  577.     char            *p;                    /* pointer into response */
  578.     char            *q;                    /* pointer into response */
  579.     char            *r;                    /* pointer into response */
  580.     short            len;                    /* string length */
  581.     short            i;                        /* loop index */
  582.     FieldInfo    *f;                    /* pointer to field info record */
  583.     Handle        fText;                /* handle to field text */
  584.     short            fSize;                /* field size */
  585.     short            code;                    /* server response code */
  586.     static short tryQueries[] = {phAliasQuery,phIdQuery,phNameQuery};
  587.                                             /* try these queries to find the user */
  588.     short            trying;                /* the query we're currently trying */
  589.     
  590.     trying = 0;
  591.     while (true) {
  592.         GetIndString(tmpl, serverCmds, tryQueries[trying++]);
  593.         utl_PlugParams(tmpl, cmd, user, nil, nil, nil);
  594.         if (rCode = DoCommand(cmd)) return rCode;
  595.         p = *Response+1;
  596.         code = atoi(p);
  597.         if (code == phNoMatches) {
  598.             if (sizeof(tryQueries)/sizeof(short)==trying) break;
  599.             continue;
  600.         }
  601.         HLock(Response);
  602.         HLock((Handle)fields);
  603.         while (true) {
  604.             code = atoi(p);
  605.             if (code == -phSuccess) {
  606.                 code = phSyntax;
  607.                 if (Skip(&p)) break;
  608.                 if (*p != '1') {
  609.                     code = phManyMatches;
  610.                     break;
  611.                 };
  612.                 if (Skip(&p)) break;
  613.                 q = strpbrk(p, ":\r");
  614.                 if (!q || *q == '\r') break;
  615.                 p += strspn(p, " \t");
  616.                 if (p == q) break;
  617.                 *q = 0;
  618.                 for (i = 0; i < numFields; i++) {
  619.                     f = &(*fields)[i];
  620.                     if (!strcmp(f->name+1, p)) break;
  621.                 }
  622.                 if (i == numFields) break;
  623.                 fText = f->original;
  624.                 SetHandleSize(fText, 0);
  625.                 fSize = 0;
  626.                 while (true) {
  627.                     p = q+1;
  628.                     if (*p == ' ') p++;
  629.                     q = strchr(p, '\r');
  630.                     if (!q) break;
  631.                     len = q-p;
  632.                     SetHandleSize(fText, fSize+len+1);
  633.                     memcpy(*fText+fSize, p, len);
  634.                     fSize += len;
  635.                     *(*fText+fSize) = '\n';
  636.                     fSize++;
  637.                     p = q+1;
  638.                     code = atoi(p);
  639.                     if (code != -phSuccess) break;
  640.                     code = phSyntax;
  641.                     if (Skip(&p)) break;
  642.                     if (Skip(&p)) break;
  643.                     code = -phSuccess;
  644.                     if (*p != ':') break;
  645.                     q = p;
  646.                 }
  647.                 if (code == phSyntax) break;
  648.                 r = *fText+fSize-1;
  649.                 while (r >= *fText && *r == '\n') r--;
  650.                 fSize = r - *fText + 1;
  651.                 SetHandleSize(fText, fSize);
  652.                 f->origSize = fSize;
  653.             } else if (code == phMatchCount || code == -phViewEncrypt) {
  654.                 q = strchr(p, '\r');
  655.                 code = phSyntax;
  656.                 if (!q) break;
  657.             } else {
  658.                 break;
  659.             }
  660.             p = q+1;
  661.         }
  662.         HUnlock(Response);
  663.         HUnlock((Handle)fields);
  664.         break;
  665.     }
  666.     *sCode = abs(code);
  667.     return noErr;
  668. }
  669.  
  670. /*_____________________________________________________________________
  671.  
  672.     Quit - Sent Quit Command to Server.
  673. _____________________________________________________________________*/
  674.  
  675. static OSErr Quit (void)
  676.  
  677. {
  678.     Str255        cmd;                    /* server command */
  679.     
  680.     GetIndString(cmd, serverCmds, phQuit);
  681.     return DoCommand(cmd);
  682. }
  683.  
  684. /*_____________________________________________________________________
  685.  
  686.     DoChangeCommand - Send Change Command to Server.
  687.     
  688.     Entry:    tmpl = change command template.
  689.                 alias = alias of record to change.
  690.                 fName = name of field to change.
  691.                 val = handle to  new field value.
  692.                 len = length of new field value.
  693.                 
  694.     Exit:        function result = error code.
  695.                 sCode = server response code:
  696.                     phSuccess = success.
  697.                     phNoAuthField = not authorized to change this field.
  698.                     phDupAlias = duplicate alias.
  699.                     phIllegalVal = illegal value.
  700.                     other = unexpected.
  701.                 servErrMsg = server error message if sCode != phSuccess.
  702.  
  703. change alias="j-blow" make other="test"
  704. 200:1 entry changed.
  705.  
  706. change alias="j-blow" make hero="yes"
  707. -505:hero:you may not change this field.
  708. 500:1 entry found, none changed.
  709.  
  710. change alias="j-blow" make alias="c-nims"
  711. -509:Alias c-nims conflicts with other users.
  712. 500:1 entry found, none changed.
  713.  
  714. change alias="j-blow" make alias=""
  715. -512:Alias is too long or too short.
  716. 500:1 entry found, none changed.
  717. _____________________________________________________________________*/
  718.  
  719. static OSErr DoChangeCommand (Str255 tmpl, Str255 alias, Str255 fName,
  720.     Handle val, short len, short *sCode, Str255 servErrMsg)
  721.     
  722. {
  723.     OSErr            rCode;                /* result code */
  724.     Str255        cmd;                    /* server command */
  725.     long            cmdSize;                /* size of server command buffer */
  726.     short            cmdLen;                /* length of server command */
  727.     char            *p;                    /* pointer into server command */
  728.     char            *q;                    /* pointer into field text */
  729.     char            *qEnd;                /* pointer to end of field text */
  730.     long            pOffset;                /* offset of p from beginning of command buffer */
  731.  
  732.     utl_PlugParams(tmpl, cmd, alias, fName, nil, nil);
  733.     cmdSize = GetHandleSize(Command);
  734.     cmdLen = *cmd;
  735.     HLock(Command);
  736.     HLock(val);
  737.     p = *Command;
  738.     memcpy(p, cmd+1, cmdLen);
  739.     p += cmdLen;
  740.     qEnd = *val + len;
  741.     for (q = *val; q < qEnd; q++) {
  742.         if (cmdLen + 2 > cmdSize) {
  743.             cmdSize += cmdHandleInc;
  744.             pOffset = p - *Command;
  745.             HUnlock(Command);
  746.             SetHandleSize(Command, cmdSize);
  747.             HLock(Command);
  748.             p = *Command + pOffset;
  749.         }
  750.         if (*q=='\n' || *q=='\t' || *q=='"' || *q=='\\') {
  751.             *p++ = '\\';
  752.             switch (*q) {
  753.                 case '\n': *p++ = 'n'; break;
  754.                 case '\t': *p++ = 't'; break;
  755.                 case '"': *p++ = '"'; break;
  756.                 case '\\': *p++ = '\\'; break;
  757.             }
  758.             cmdLen += 2;
  759.         } else {
  760.             *p++ = *q;
  761.             cmdLen++;
  762.         }
  763.     }
  764.     *p++ = '"';
  765.     *p++ = '\r';
  766.     HUnlock(Command);
  767.     HUnlock(val);
  768.     if (rCode = mtcp_PhCommand(Command, Response, &Truncated)) return rCode;
  769.     *sCode = abs(atoi(*Response+1));
  770.     if (*sCode != phSuccess) GetServErrMsg(*Response+1, servErrMsg);
  771.     return noErr;
  772. }
  773.  
  774. /*_____________________________________________________________________
  775.  
  776.     CreateNewFields - Create Name and Type Fields for New Record.
  777.     
  778.     Entry:    alias = alias of record to change.
  779.                 name = name field.
  780.                 type = type field.
  781.                 
  782.     Exit:        function result = error code.
  783.                 sCode = server response code:
  784.                     phSuccess = success.
  785.                     phIllegalVal = illegal value.
  786.                     other = unexpected.
  787.                 whichField = illegal field number if sCode != phSuccess:
  788.                     1 = name.
  789.                     2 = type.
  790.                 servErrMsg = server error message if sCode != phSuccess.
  791. _____________________________________________________________________*/
  792.  
  793. static OSErr CreateNewFields (Str255 alias, Str255 name, 
  794.     Str255 type, short *sCode, short *whichField, Str255 servErrMsg)
  795.     
  796. {
  797.     Str255            tmpl;                /* server command template */
  798.     Handle            val;                /* handle to field value */
  799.     short                len;                /* length of field value */
  800.     OSErr                rCode;            /* result code */
  801.     Str255            fName;            /* field name */
  802.     
  803.     GetIndString(tmpl, serverCmds, phMake);
  804.     rCode = noErr;
  805.     *whichField = 1;
  806.     if (len = *name) {
  807.         GetIndString(fName, fieldNames, nameFieldName);
  808.         PtrToHand(name+1, &val, len);
  809.         rCode = DoChangeCommand(tmpl, alias, fName, val, 
  810.             len, sCode, servErrMsg);
  811.         DisposHandle(val);
  812.         if (rCode) return rCode;
  813.         if (*sCode != phSuccess) return noErr;
  814.     }
  815.     *whichField = 2;
  816.     if (len = *type) {
  817.         GetIndString(fName, fieldNames, typeFieldName);
  818.         PtrToHand(type+1, &val, len);
  819.         rCode = DoChangeCommand(tmpl, alias, fName, val, 
  820.             len, sCode, servErrMsg);
  821.         DisposHandle(val);
  822.     }
  823.     return rCode;
  824. }
  825.  
  826. /*_____________________________________________________________________
  827.  
  828.     GetProxyList - Get Proxy List.
  829.     
  830.     Entry:    alias = alias.
  831.     
  832.     Exit:        function result = error code.
  833.                 proxyList = handle to list of aliases for which the
  834.                     alias is a proxy, as a sequence of Pascal strings.
  835.                 sCode = server response code to proxy query:
  836.                     phSuccess = success.
  837.                     phManyMatches = too many matches.
  838.                     phNoAuthSearch = field does not have Lookup attribute.
  839.                     phNoInxField = field does not have Indexed attribute.
  840.                     other = unexpected.
  841.                 
  842. query proxy=j-blow return alias
  843. 102:There were 2 matches to your request.
  844. -200:1:   alias: j-doe
  845. -200:2:   alias: j-smith
  846. 200:Ok.
  847.  
  848. query proxy=zzz return alias
  849. 501:No matches to your query.
  850.  
  851. query proxy=j-blow return alias
  852. 502:Too many entries to print.
  853.  
  854. query proxy="j-blow" return alias
  855. -504:proxy:you may not use this field for lookup.
  856. -515:no non-null key field in query.
  857. 500:Did not understand query.
  858.  
  859. query proxy="j-blow" return alias
  860. -515:no non-null key field in query.
  861. 500:Did not understand query.
  862. _____________________________________________________________________*/
  863.  
  864. static OSErr GetProxyList (Str255 alias, Handle *proxyList, short *sCode)
  865.  
  866. {
  867.     Str255        tmpl;                    /* query template */
  868.     Str255        cmd;                    /* query command */
  869.     char            *p;                    /* pointer into response */
  870.     char            *q;                    /* pointer into response */
  871.     short            len;                    /* length of alias */
  872.     Handle        pList;                /* handle to proxy list */
  873.     short            pSize;                /* size of proxy list */
  874.     OSErr            rCode;                /* result code */
  875.     short            code;                    /* server result code */
  876.     
  877.     pList = NewHandle(0);
  878.     pSize = 0;
  879.     GetIndString(tmpl, serverCmds, phProxyQuery);
  880.     utl_PlugParams(tmpl, cmd, alias, nil, nil, nil);
  881.     if (rCode = DoCommand(cmd)) return rCode;
  882.     HLock(Response);
  883.     p = *Response+1;
  884.     while (true) {
  885.         code = atoi(p);
  886.         if (code == phSuccess) break;
  887.         if (code == -phSuccess) {
  888.             code = phSyntax;
  889.             if (Skip(&p)) break;
  890.             if (Skip(&p)) break;
  891.             if (Skip(&p)) break;
  892.             q = strpbrk(p, ":\r");
  893.             p += strspn(p, " \t");
  894.             if (p == q) break;
  895.             *q = 0;
  896.             len = q-p;
  897.             SetHandleSize(pList, pSize + len + 1);
  898.             *(*pList + pSize) = len;
  899.             memcpy(*pList+pSize+1, p, len);
  900.             pSize += len+1;
  901.         } else if (code == phMatchCount) {
  902.             q = strchr(p, '\r');
  903.             if (!q) break;
  904.         } else {
  905.             break;
  906.         }
  907.         p = q+1;
  908.     }
  909.     HUnlock(Response);
  910.     *proxyList = pList;
  911.     if (code == phNoMatches) code = phSuccess;
  912.     *sCode = abs(code);
  913.     return noErr;
  914. }
  915.  
  916. /*_____________________________________________________________________
  917.  
  918.     PutFieldData - Put Changed Fields to Server.
  919.     
  920.     Entry:    alias = alias.
  921.                 fields = handle to field info array.
  922.                 numfields = number of fields.
  923.                 
  924.     Exit:        function result = error code.
  925. _____________________________________________________________________*/
  926.  
  927. static OSErr PutFieldData (Str255 alias, FieldInfo **fields, short numFields)
  928.  
  929. {
  930.     OSErr            rCode;                /* result code */
  931.     Str255        tmpl;                    /* server command template */
  932.     short            i;                        /* loop index */
  933.     FieldInfo    *f;                    /* pointer to field info record */
  934.     Handle        fText;                /* handle to field text */
  935.     short            teLength;            /* length of field text */
  936.     short            code;                    /* server response code */
  937.     Str255        realAlias;            /* the real alias */
  938.     Str255        aliasFName;            /* name of alias field */
  939.     short            nLines;                /* number of lines */
  940.     short            *lineStarts;        /* pointer into lineStarts array */
  941.     Handle        val;                    /* handle to wrapped field data */
  942.     char            *p;                    /* pointer into unwrapped field data */
  943.     char            *q;                    /* pointer into wrapped field data */
  944.     short            len;                    /* length of text */
  945.     short            j;                        /* loop index */
  946.     Str255        servErrMsg;            /* server error message */
  947.     
  948.     GetIndString(tmpl, serverCmds, phMake);
  949.     GetIndString(aliasFName, fieldNames, aliasFieldName);
  950.     rCode = noErr;
  951.     HLock((Handle)fields);
  952.     utl_CopyPString(realAlias, alias);
  953.     for (i = 0; i < numFields; i++) {
  954.         f = &(*fields)[i];
  955.         if (!f->dirty) continue;
  956.         fText = (**f->edit).hText;
  957.         teLength = (**f->edit).teLength;
  958.         nLines = (**f->edit).nLines;
  959.         lineStarts = (**f->edit).lineStarts;
  960.         val = NewHandle(teLength + nLines);
  961.         if (nLines) {
  962.             p = *fText;
  963.             q = *val;
  964.             for (j = 0; j < nLines; j++) {
  965.                 len = *(lineStarts+1) - *lineStarts;
  966.                 memcpy(q, p, len);
  967.                 p += len;
  968.                 q += len;
  969.                 lineStarts++;
  970.                 if (*(q-1) != '\n') *q++ = '\n';
  971.             }
  972.             q--;
  973.             while (q >= *val && *q == '\n') q--;
  974.             len = q - *val + 1;
  975.         } else {
  976.             len = 0;
  977.         }
  978.         rCode = DoChangeCommand(tmpl, realAlias, f->name, val, len, &code, servErrMsg);
  979.         if (rCode) break;
  980.         if (code == phSuccess) {
  981.             f->putOK = true;
  982.             if (EqualString(f->name, aliasFName, true, true)) {
  983.                 *realAlias = len;
  984.                 memcpy(realAlias+1, *val, len);
  985.             }
  986.         } else {
  987.             f->servErrMsg = NewHandle(*servErrMsg + 1);
  988.             utl_CopyPString(*(f->servErrMsg), servErrMsg);
  989.         }
  990.         DisposHandle(val);
  991.         val = nil;
  992.     }
  993.     HUnlock((Handle)fields);
  994.     return rCode;
  995. }
  996.  
  997. /*_____________________________________________________________________
  998.  
  999.     ChangePassword - Change Ph Password.
  1000.     
  1001.     Entry:    alias = alias of record to change.
  1002.                 newPswd = new password.
  1003.                 
  1004.     Exit:        function result = error code.
  1005.                 sCode = server response code:
  1006.                     phSuccess = success.
  1007.                     phIllegalVal = illegal value.
  1008.                     other = unexpected.
  1009.                 servErrMsg = server error message if sCode != phSuccess.
  1010.  
  1011. change alias="j-blow" make password="xxx"
  1012. 200:1 entry changed.
  1013.  
  1014. change alias="j-blow" make password="xxx"
  1015. -512:Passwords must use only printable characters; sorry.
  1016. -512:If your password met this rule, reissue your login command, and try again.
  1017. 500:1 entry found, none changed.
  1018. _____________________________________________________________________*/
  1019.  
  1020. static OSErr ChangePassword (Str255 alias, Str255 newPswd, short *sCode, 
  1021.     Str255 servErrMsg)
  1022.     
  1023. {
  1024.     OSErr                rCode;                    /* result code */
  1025.     Str255            tmpl;                        /* server command template */
  1026.     Str255            cmd;                        /* server command */
  1027.     short                encryptLen;                /* length of encrypted string */
  1028.  
  1029.     GetIndString(tmpl, serverCmds, phChPswdCmd);
  1030.     utl_PlugParams(tmpl, cmd, alias, nil, nil, nil);
  1031.     p2cstr(newPswd);
  1032.     encryptLen = encryptit(cmd + *cmd + 1, newPswd);
  1033.     c2pstr(newPswd);
  1034.     *cmd += encryptLen;
  1035.     if (rCode = DoCommand(cmd)) return rCode;
  1036.     *sCode = abs(atoi(*Response+1));
  1037.     if (*sCode != phSuccess) GetServErrMsg(*Response+1, servErrMsg);
  1038.     return noErr;
  1039. }
  1040.  
  1041. /*_____________________________________________________________________
  1042.  
  1043.     CreateRecord - Create Ph Record.
  1044.     
  1045.     Entry:    alias = alias for new record.
  1046.                 
  1047.     Exit:        function result = error code.
  1048.                 sCode = server response code:
  1049.                     phSuccess = success.
  1050.                     phDupAlias = duplicate alias.
  1051.                     phIllegalVal = illegal value.
  1052.                     other = unexpected.
  1053.                 servErrMsg = server error message if sCode != phSuccess.
  1054.  
  1055. add alias="w-xyz"
  1056. 200:Ok.
  1057.  
  1058. add alias="c-nims"
  1059. -509:Alias c-nims in use or is too common a name.
  1060. 599:Add command not understood.
  1061. _____________________________________________________________________*/
  1062.  
  1063. static OSErr CreateRecord (Str255 alias, short *sCode, Str255 servErrMsg)
  1064.     
  1065. {
  1066.     OSErr                rCode;                    /* result code */
  1067.     Str255            tmpl;                        /* server command template */
  1068.     Str255            cmd;                        /* server command */
  1069.  
  1070.     GetIndString(tmpl, serverCmds, phNewRecordCmd);
  1071.     utl_PlugParams(tmpl, cmd, alias, nil, nil, nil);
  1072.     if (rCode = DoCommand(cmd)) return rCode;
  1073.     *sCode = abs(atoi(*Response+1));
  1074.     if (*sCode != phSuccess) GetServErrMsg(*Response+1, servErrMsg);
  1075.     return noErr;
  1076. }
  1077.  
  1078. /*_____________________________________________________________________
  1079.  
  1080.     DeleteRecord - Delete Ph Record.
  1081.     
  1082.     Entry:    alias = alias of record to be deleted.
  1083.                 
  1084.     Exit:        function result = server response code.
  1085.                 sCode = server response code:
  1086.                     phSuccess = success.
  1087.                     phNoMatches = no such alias.
  1088.                     other = unexpected.
  1089.                     
  1090. delete alias="w-xyz"
  1091. 200:1 entries deleted.
  1092.  
  1093. delete alias="xyz"
  1094. 501:No entries matched specifications.
  1095. _____________________________________________________________________*/
  1096.  
  1097. static OSErr DeleteRecord (Str255 alias, short *sCode)
  1098.  
  1099. {
  1100.     Str255            tmpl;                /* server command template */
  1101.     Str255            cmd;                /* server command */
  1102.     OSErr                rCode;            /* result code */
  1103.     
  1104.     GetIndString(tmpl, serverCmds, phDeleteCmd);
  1105.     utl_PlugParams(tmpl, cmd, alias, nil, nil, nil);
  1106.     if (rCode = DoCommand(cmd)) return rCode;
  1107.     *sCode = abs(atoi(*Response+1));
  1108.     return noErr;
  1109. }
  1110.  
  1111. /*_____________________________________________________________________
  1112.  
  1113.     AdjustBigFields - Adjust Big Fields.
  1114.     
  1115.     Entry:    reply = handle to query reply.
  1116.                 replyLen = length of query reply.
  1117.                 
  1118.     Exit:        fields adjusted.
  1119.     
  1120.     This function checks for lines in the reply which exceed 80 characters
  1121.     in length. Any fields which contain such lines are reformatted as
  1122.     follows:
  1123.     
  1124.                     field_name:
  1125.     field text field text field text field text field text field text 
  1126.     field text field text field text field text field text field text 
  1127.     field text field text field text field text field text field text 
  1128.     field text field text field text field text field text field text 
  1129.     
  1130.     This function also truncates the reply to 32K if necessary.
  1131. _____________________________________________________________________*/
  1132.  
  1133. static void AdjustBigFields (Handle reply, long replyLen)
  1134.  
  1135. {
  1136.     char        *p;            /* pointer into reply */
  1137.     char        *pEnd;        /* pointer to end of reply */
  1138.     char        *q;            /* pointer into reply */
  1139.     char        *r;            /* pointer into reply */
  1140.     char        *z;            /* pointer into reply */
  1141.     Str255    msg;            /* truncation message */
  1142.     
  1143.     HLock(reply);
  1144.     p = *reply;
  1145.     pEnd = *reply + replyLen;
  1146.     z = *reply;
  1147.     while (p < pEnd) {
  1148.         q = strchr(p, '\n');
  1149.         if (q - p > 80) {
  1150.             r = p;
  1151.             while (true) {
  1152.                 q = r + strspn(r, " ");
  1153.                 if (*q != ':') break;
  1154.                 r--;
  1155.                 while (*--r != '\n' && r > *reply);
  1156.                 r++;
  1157.                 if (r <= *reply) break;
  1158.             }
  1159.             if (r > *reply) {
  1160.                 /* r pts to first char of first line of field, q pts to first char
  1161.                     of field name in first line. */
  1162.                 q = strchr(q, ':') + 1;
  1163.                 if (p < q && z != p) memmove(z, p, q-p);
  1164.                 z += q-p;
  1165.                 *z++ = '\n';
  1166.                 while (true) {
  1167.                     p = q+1;
  1168.                     q = strchr(p, '\n') + 1;
  1169.                     if (z != p) memmove(z, p, q-p);
  1170.                     z += q-p;
  1171.                     p = q;
  1172.                     if (p >= pEnd) break;
  1173.                     q = p + strspn(p, " ");
  1174.                     if (*q != ':') break;
  1175.                     q++;
  1176.                 }
  1177.                 continue;
  1178.             }
  1179.         }
  1180.         if (z != p) memmove(z, p, q-p+1);
  1181.         z += q-p+1;
  1182.         p = q+1;
  1183.     }
  1184.     while (*--z == '\n' && z > *reply);
  1185.     z++;
  1186.     replyLen = z - *reply;
  1187.     if (replyLen > 0x3fff || Truncated) {
  1188.         GetIndString(msg, stringsID, truncMsg);
  1189.         z = *reply + 0x3fff;
  1190.         while (*z != '\n' && z + *msg + 2 - *reply > 0x3fff) z--;
  1191.         memcpy(z+1, msg+1, *msg);
  1192.         z += *msg+1;
  1193.         *z++ = '\n';
  1194.         replyLen = z - *reply;
  1195.     }
  1196.     HUnlock(reply);
  1197.     SetHandleSize(reply, replyLen);
  1198. }
  1199.  
  1200. /*_____________________________________________________________________
  1201.  
  1202.     ExpandReply - Expand Reply Block.
  1203.     
  1204.     Entry:    reply = handle to locked reply block.
  1205.                 *z = pointer into reply.
  1206.                 len = number of bytes needed in reply.
  1207.                 *replyLen = current size of reply block.
  1208.                 
  1209.     Exit:        Relocatable block size increased if necessary to make
  1210.                 room for len more bytes plus another 1000 bytes of slop.
  1211. _____________________________________________________________________*/
  1212.  
  1213. static void ExpandReply (Handle reply, char **z, long len, long *replyLen)
  1214.  
  1215. {
  1216.     long            offset;            /* offset into block of pointer */
  1217.  
  1218.     if (*z + len + 1000 < *reply + *replyLen) return;
  1219.     offset = *z - *reply;
  1220.     HUnlock(reply);
  1221.     *replyLen = offset + len + 1000;
  1222.     SetHandleSize(reply, *replyLen);
  1223.     HLock(reply);
  1224.     *z = *reply + offset;
  1225. }
  1226.  
  1227. /*_____________________________________________________________________
  1228.  
  1229.     SendQuery - Send a Query.
  1230.     
  1231.     Entry:    query = handle to query.
  1232.                 queryLen = length of query.
  1233.                 emailDomain = email domain name, empty string if none.
  1234.                 reply = preallocated reply handle.
  1235.                 
  1236.     Exit:        function result = error code.
  1237.                 reply = handle to reply, size adjusted to exactly hold
  1238.                     the reply.
  1239.                 
  1240.                 If server errors occur, the server error messages are
  1241.                 placed in the reply. The match count message, if any,
  1242.                 is also placed in the reply.
  1243.                     
  1244. query john norstad
  1245. 102:There was 1 match to your request.
  1246. -200:1:           alias: j-norstad
  1247. -200:1:            name: John Norstad
  1248. -200:1:           email: jln@casbah.acns.nwu.edu
  1249. -200:1:           phone: (708)491-4077
  1250. -200:1:         address: Academic Computing and Network Services
  1251. -200:1:                : 2129 Sheridan Road
  1252. -200:1:                : Evanston, IL 60208
  1253. -200:1: office_location: Vogelback Room 125
  1254. -200:1:      department: ACNS, Networking & Communications
  1255. -200:1:           title: Network Analyst
  1256. -200:1:           hours: My hours vary widely.  I usually work days, but not always.
  1257. -200:1:                : Your best bet is 10-4 M-F.
  1258. -200:1:           other: Mac networking, programming, and virus guru.
  1259. -200:1:                : Author of the "Disinfectant" Mac anti-viral utility.
  1260. 200:Ok.
  1261.  
  1262. query john
  1263. 102:There were 3 matches to your request.
  1264. -200:1:           alias: j-norstad
  1265. -200:1:            name: John Norstad
  1266. -200:1:           email: jln@casbah.acns.nwu.edu
  1267. -200:1:           phone: (708)491-4077
  1268. -200:1:         address: Academic Computing and Network Services
  1269. -200:1:                : 2129 Sheridan Road
  1270. -200:1:                : Evanston, IL 60208
  1271. -200:1: office_location: Vogelback Room 125
  1272. -200:1:      department: ACNS, Networking & Communications
  1273. -200:1:           title: Network Analyst
  1274. -200:1:           hours: My hours vary widely.  I usually work days, but not always.
  1275. -200:1:                : Your best bet is 10-4 M-F.
  1276. -200:1:           other: Mac networking, programming, and virus guru.
  1277. -200:1:                : Author of the "Disinfectant" Mac anti-viral utility.
  1278. -200:2:           alias: J-Franks
  1279. -200:2:            name: John Franks
  1280. -200:2:           email: john@math.nwu.edu
  1281. -200:2:           phone: (708) 491-5548
  1282. -200:2:         address: Department of Mathematics
  1283. -200:2:                : Northwestern University
  1284. -200:2:                : Evanston, IL 60208-2370
  1285. -200:2:      department: Mathematics
  1286. -200:2:           title: Professor of Mathematics
  1287. -200:3:           alias: j-smith
  1288. -200:3:            name: John Smith
  1289. 200:Ok.
  1290.  
  1291. query mary
  1292. 501:No matches to your query.
  1293.  
  1294. query xxx=yyy
  1295. -507:xxx:unknown field.
  1296. -515:no non-null key field in query.
  1297. 500:Did not understand query.
  1298.  
  1299. query title=abc
  1300. -515:no non-null key field in query.
  1301. 500:Did not understand query.
  1302.  
  1303. query john
  1304. 502:Too many entries to print.
  1305. _____________________________________________________________________*/
  1306.  
  1307. static OSErr SendQuery (Handle query, unsigned short queryLen, 
  1308.     Str255 emailDomain, Handle reply)
  1309.  
  1310. {
  1311.     Str255            cmd;                /* "query " command prefix */
  1312.     Str255            aliasStr;        /* "alias" field name */
  1313.     Str255            emailStr;        /* "email" field name */
  1314.     Str255            alias;            /* last alias seen */
  1315.     Str255            msg;                /* error message */
  1316.     short                aliasIndex;        /* index of last alias seen, or 0 if none */
  1317.     long                cmdSize;            /* size of command buffer */
  1318.     OSErr                rCode;            /* error code */
  1319.     char                *p;                /* pointer into response */
  1320.     char                *q;                /* pointer into response */
  1321.     char                *r;                /* pointer into response */
  1322.     char                *z;                /* pointer into reply */
  1323.     Boolean            syntaxErr;        /* true if syntax error in response */
  1324.     short                lastIndex;        /* last match entry index seen */
  1325.     short                index;            /* current match entry index */
  1326.     short                code;                /* server response code */
  1327.     short                i;                    /* loop index */
  1328.     short                len;                /* length of text */
  1329.     long                replyLen;        /* length of reply */
  1330.  
  1331.     GetIndString(cmd, serverCmds, phQuery);
  1332.     GetIndString(aliasStr, fieldNames, aliasFieldName);
  1333.     GetIndString(emailStr, fieldNames, emailFieldName);
  1334.     cmdSize = GetHandleSize(Command);
  1335.     if (*cmd + queryLen + 1 > cmdSize) SetHandleSize(Command, *cmd + queryLen + 1);
  1336.     memcpy(*Command, cmd+1, *cmd);
  1337.     memcpy(*Command + *cmd, *query, queryLen);
  1338.     *(*Command + *cmd + queryLen) = '\r';
  1339.     rCode = mtcp_PhCommand(Command, Response, &Truncated);
  1340.     if (rCode == mtcpCancel) {
  1341.         SetHandleSize(reply, 0);
  1342.         return rCode;
  1343.     }
  1344.     replyLen = GetHandleSize(Response) + 2000;
  1345.     SetHandleSize(reply, replyLen); 
  1346.     HLock(Response);
  1347.     HLock(reply);
  1348.     p = *Response+1;
  1349.     z = *reply;
  1350.     syntaxErr = false;
  1351.     lastIndex = 0;
  1352.     aliasIndex = 0;
  1353.     while (true) {
  1354.         ExpandReply(reply, &z, 1000, &replyLen);
  1355.         code = atoi(p);
  1356.         if (code == phSuccess) {
  1357.             break;
  1358.         } else if (code == -phSuccess) {
  1359.             if (syntaxErr = Skip(&p)) break;
  1360.             index = atoi(p);
  1361.             if (syntaxErr = !index) break;
  1362.             if (index != lastIndex) {
  1363.                 lastIndex = index;
  1364.                 for (i = 0; i < 79; i++) *z++ = '-';
  1365.                 *z++ = '\n';
  1366.             }
  1367.             p = strpbrk(p, ":\r");
  1368.             if (syntaxErr = (!p || *p != ':')) break;
  1369.             p++;
  1370.             q = strpbrk(p, ":\r");
  1371.             if (syntaxErr = !q) break;
  1372.             if (*emailDomain && *q == ':') {
  1373.                 if (!strncmp(aliasStr+1, q-*aliasStr, *aliasStr)) {
  1374.                     q++;
  1375.                     q += strspn(q, " \t");
  1376.                     r = strpbrk(q, " \t\r");
  1377.                     len = r-q;
  1378.                     if (len > 255) len = 255;
  1379.                     if (len) {
  1380.                         aliasIndex = index;
  1381.                         *alias = len;
  1382.                         memcpy(alias+1, q, len);
  1383.                     }
  1384.                 } else if (index == aliasIndex &&
  1385.                     !strncmp(emailStr+1, q-*emailStr, *emailStr)) {
  1386.                     q++;
  1387.                     len = q-p;
  1388.                     ExpandReply(reply, &z, len, &replyLen);
  1389.                     memcpy(z, p, len);
  1390.                     z += len;
  1391.                     *z++ = ' ';
  1392.                     memcpy(z, alias+1, *alias);
  1393.                     z += *alias;
  1394.                     *z++ = '@';
  1395.                     memcpy(z, emailDomain+1, *emailDomain);
  1396.                     z += *emailDomain;
  1397.                     *z++ = '\n';
  1398.                     for (i = 1; i < len; i++) *z++ = ' ';
  1399.                     *z++ = ':';
  1400.                     *z++ = ' ';
  1401.                     *z++ = '(';
  1402.                     q += strspn(q, " \t");
  1403.                     p = q;
  1404.                     q = strchr(p, '\r');
  1405.                     if (syntaxErr = !q) break;
  1406.                     len = q-p;
  1407.                     ExpandReply(reply, &z, len, &replyLen);
  1408.                     memcpy(z, p, len);
  1409.                     z += len;
  1410.                     *z++ = ')';
  1411.                     *z++ = '\n';
  1412.                     p = q+1;
  1413.                     continue;
  1414.                 }
  1415.             }
  1416.             q = strchr(p, '\r');
  1417.             if (syntaxErr = !q) break;
  1418.             len = q-p;
  1419.             ExpandReply(reply, &z, len, &replyLen);
  1420.             memcpy(z, p, len);
  1421.             z += len;
  1422.             *z++ = '\n';
  1423.         } else if (code == phMatchCount) {
  1424.             if (syntaxErr = Skip(&p)) break;
  1425.             q = strchr(p, '\r');
  1426.             if (syntaxErr = !q) break;
  1427.             len = q-p;
  1428.             ExpandReply(reply, &z, len, &replyLen);
  1429.             memcpy(z, p, len);
  1430.             z += len;
  1431.             *z++ = '\n';
  1432.         } else {
  1433.             p = strpbrk(p, ":\r");
  1434.             if (syntaxErr = !p || *p != ':') break;
  1435.             p++;
  1436.             q = strchr(p, '\r');
  1437.             if (syntaxErr = !q) break;
  1438.             len = q-p;
  1439.             ExpandReply(reply, &z, len, &replyLen);
  1440.             memcpy(z, p, len);
  1441.             z += len;
  1442.             *z++ = '\n';
  1443.             if (code > 0) break;
  1444.         }
  1445.         p = q+1;
  1446.     }
  1447.     if (syntaxErr && (!Truncated || p)) {
  1448.         GetIndString(msg, servErrors, msgSyntaxError);
  1449.         memcpy(z, msg+1, *msg);
  1450.         z += *msg;
  1451.         *z++ = '\n';
  1452.     }
  1453.     replyLen = z - *reply;
  1454.     HUnlock(Response);
  1455.     HUnlock(reply);
  1456.     AdjustBigFields(reply, replyLen);
  1457.     return rCode;
  1458. }
  1459.  
  1460. /*_____________________________________________________________________
  1461.  
  1462.     GetHelp - Get Server Help Text.
  1463.     
  1464.     Entry:    topic = help topic.
  1465.                 reply = preallocated reply handle.
  1466.                 
  1467.     Exit:        function result = error code.
  1468.                 reply = handle to reply, size adjusted to exactly hold
  1469.                     the reply.
  1470.                 
  1471.                 If server errors occur, the server error messages are
  1472.                 placed in the reply.
  1473.                 
  1474. help macph 02.Server_Requirements
  1475. -200:1:02.Server_Requirements:
  1476. -200:1: Server Requirements
  1477. -200:1: ===================
  1478. -200:1:
  1479. -200:1: This section is for Ph server administrators. It describes in detail the
  1480. ...
  1481. -200:1: Detailed mod to be presented here, along with instructions on how to convert
  1482. -200:1: an existing database to index the proxy field.
  1483. 200:Ok.
  1484. _____________________________________________________________________*/
  1485.  
  1486. static OSErr GetHelp (Str255 topic, Handle reply)
  1487.  
  1488. {
  1489.     Str255            cmd;                /* server command */
  1490.     Str255            msg;                /* error message */
  1491.     OSErr                rCode;            /* error code */
  1492.     char                *p;                /* pointer into response */
  1493.     char                *q;                /* pointer into response */
  1494.     char                *r;                /* pointer into response */
  1495.     char                *s;                /* pointer into response */
  1496.     char                *z;                /* pointer into reply */
  1497.     Boolean            syntaxErr;        /* true if syntax error in response */
  1498.     short                code;                /* server response code */
  1499.     short                len;                /* length of text */
  1500.     short                replyLen;        /* length of reply */
  1501.     Boolean            strip;            /* true to strip leading blanks */
  1502.  
  1503.     GetIndString(cmd, serverCmds, HelpNative ? phHelpNative : phHelp);
  1504.     memcpy(cmd + *cmd + 1, topic+1, *topic);
  1505.     *cmd += *topic;
  1506.     if (rCode = DoCommand(cmd)) return rCode;
  1507.     SetHandleSize(reply, GetHandleSize(Response) + 2000);
  1508.     HLock(Response);
  1509.     HLock(reply);
  1510.     p = *Response+1;
  1511.     z = *reply;
  1512.     syntaxErr = strip = false;
  1513.     while (true) {
  1514.         code = atoi(p);
  1515.         if (code == phSuccess) {
  1516.             break;
  1517.         } else if (code == -phSuccess) {
  1518.             if (p == *Response+1) {
  1519.                 q = p;
  1520.                 if (syntaxErr = Skip(&q)) break;
  1521.                 q = strpbrk(q, ":\r");
  1522.                 if (syntaxErr = (!q || *q != ':')) break;
  1523.                 q++;
  1524.                 r = strchr(q, '\r');
  1525.                 if (syntaxErr = !r) break;
  1526.                 s = r-1;
  1527.                 while (s > q && *s == ' ') s--;
  1528.                 len = s-q;
  1529.                 if (len == *topic && *s == ':' && !strncmp(topic+1, q, *topic)) {
  1530.                     p = r+1;
  1531.                     strip = true;
  1532.                     continue;
  1533.                 }
  1534.             }
  1535.             if (syntaxErr = Skip(&p)) break;
  1536.             p = strpbrk(p, ":\r");
  1537.             if (syntaxErr = (!p || *p != ':')) break;
  1538.             p++;
  1539.             if (strip && *p == ' ') p++;
  1540.             q = strchr(p, '\r');
  1541.             if (syntaxErr = !q) break;
  1542.             for (r = p; r < q; r++) {
  1543.                 if ((*r == '_' && *(r+1) == 0x08) ||
  1544.                     (*r == 0x08 && *(r+1) == '_')) {
  1545.                     r++;
  1546.                 } else if (*r >= 0x20 && *r <= 0x7e) {
  1547.                     *z++ = *r;
  1548.                 }
  1549.             }
  1550.             *z++ = '\n';
  1551.         } else {
  1552.             p = strpbrk(p, ":\r");
  1553.             if (syntaxErr = !p || *p != ':') break;
  1554.             p++;
  1555.             q = strchr(p, '\r');
  1556.             if (syntaxErr = !q) break;
  1557.             len = q-p;
  1558.             memcpy(z, p, len);
  1559.             z += len;
  1560.             *z++ = '\n';
  1561.             if (code > 0) break;
  1562.         }
  1563.         p = q+1;
  1564.     }
  1565.     if (syntaxErr) {
  1566.         GetIndString(msg, servErrors, msgSyntaxError);
  1567.         memcpy(z, msg+1, *msg);
  1568.         z += *msg;
  1569.         *z++ = '\n';
  1570.     }
  1571.     while (*--z == '\n' && z > *reply);
  1572.     z++;
  1573.     *z++ = '\n';
  1574.     *z++ = '\n';
  1575.     replyLen = z - *reply;
  1576.     HUnlock(Response);
  1577.     HUnlock(reply);
  1578.     SetHandleSize(reply, replyLen);
  1579.     return noErr;
  1580. }
  1581.  
  1582. /*_____________________________________________________________________
  1583.  
  1584.     GetHelpTopics - Get Help Topic List.
  1585.     
  1586.     Entry:    server = Ph server host domain name.
  1587.     
  1588.     Exit:        function result = error code.
  1589.                 
  1590. help macph
  1591. -200:1: These ``macph'' help topics are available:
  1592. -200:1: 01.Introduction         02.Server_Requirements
  1593. -200:1: To view one of these topics, type ``help name-of-topic-you-want''.
  1594. -200:2: These ``native'' help topics are also available:
  1595. -200:2: 100             402             507             516             599
  1596. -200:2: 101             475             508             517             forwarding
  1597. -200:2: 102             500             509             518             policy
  1598. -200:2: 200             501             510             519             protocol
  1599. -200:2: 201             502             511             520             restaurant
  1600. -200:2: 300             503             512             521             site
  1601. -200:2: 301             504             513             522             update
  1602. -200:2: 400             505             514             523
  1603. -200:2: 401             506             515             598
  1604. -200:2: To view one of these topics, type ``help native name-of-topic-you-want''.
  1605. 200:Ok.
  1606.  
  1607. help macph
  1608. /usr4/lookup.Help/macph not found
  1609. -200:1:These ``macph'' help topics are available:
  1610. -200:1:To view one of these topics, type ``help name-of-topic-you-want''.
  1611. -200:2:These ``native'' help topics are also available:
  1612. -200:2:100              475             509             519             fields
  1613. -200:2:101              500             510             520             general
  1614. -200:2:102              501             511             521             lastreg
  1615. -200:2:200              502             512             522             nohelp
  1616. -200:2:201              503             513             523             policy
  1617. -200:2:300              504             514             598             restrictions
  1618. -200:2:301              505             515             599             staff
  1619. -200:2:400              506             516             campus          student
  1620. -200:2:401              507             517             class           type
  1621. -200:2:402              508             518             errors          unit
  1622. -200:2:To view one of these topics, type ``help native name-of-topic-you-want''.
  1623. 200:Ok.
  1624. _____________________________________________________________________*/
  1625.  
  1626. static OSErr GetHelpTopics (Str255 server)
  1627.  
  1628. {
  1629.     Str255            cmd;                /* server command */
  1630.     OSErr                rCode;            /* error code */
  1631.     char                *p;                /* pointer into response */
  1632.     char                 *q;                /* pointer into response */
  1633.     char                *r;                /* pointer into response */
  1634.     short                code;                /* server result code */
  1635.     short                item;                /* topic number */
  1636.     char                *topics[100];    /* array of pointers to topics */
  1637.     short                nTopics;            /* number of topics */
  1638.     Boolean            eol;                /* true if end of line */
  1639.     short                i;                    /* bubble sort loop index */
  1640.     short                j;                    /* bubble sort loop index */
  1641.     char                *temp;            /* bubble sort temp pointer */
  1642.     Boolean            allDigits;        /* true if topic name is all digits */
  1643.     Boolean            helpAvail;        /* true if help is available */
  1644.     short                len;                /* length of topic */
  1645.     short                size;                /* size of HelpTopics block */
  1646.  
  1647.     GetIndString(cmd, serverCmds, phHelp);
  1648.     if (rCode = DoCommand(cmd)) return rCode;
  1649.     helpAvail = false;
  1650.     HelpNative = false;
  1651.     nTopics = 0;
  1652.     HLock(Response);
  1653.     p = *Response+1;
  1654.     if (!rCode) {
  1655.         while (true) {
  1656.             code = atoi(p);
  1657.             if (!code) {
  1658.                 q = strchr(p, '\r');
  1659.                 if (!q) break;
  1660.                 p = q+1;
  1661.                 continue;
  1662.             }
  1663.             if (code != -phSuccess) break;
  1664.             if (Skip(&p)) break;
  1665.             code = atoi(p);
  1666.             if (code != 1 && !HelpNative) {
  1667.                 if (helpAvail) break;
  1668.                 HelpNative = true;
  1669.             }
  1670.             if (Skip(&p)) break;
  1671.             q = strpbrk(p, "'\r");
  1672.             if (!q) break;
  1673.             if (*q == '\'') {
  1674.                 q = strchr(p, '\r');
  1675.                 if (!q) break;
  1676.                 p = q+1;
  1677.                 continue;
  1678.             }
  1679.             while (true) {
  1680.                 q = strpbrk(p, " \t\r");
  1681.                 eol = *q == '\r';
  1682.                 *q = 0;
  1683.                 allDigits = true;
  1684.                 for (r = p; *r; r++) {
  1685.                     if (!isdigit(*r)) allDigits = false;
  1686.                 }
  1687.                 if (!allDigits) {
  1688.                     helpAvail = true;
  1689.                     if (nTopics < 100) topics[nTopics++] = p;
  1690.                 }
  1691.                 if (eol) break;
  1692.                 p = q+1;
  1693.                 p += strspn(p, " \t");
  1694.             }
  1695.             p = q+1;
  1696.         }
  1697.         DisposHandle(HelpTopics);
  1698.         HelpTopics = nil;
  1699.         if (helpAvail) {
  1700.             for (i = 0; i < nTopics-1; i++) {
  1701.                 for (j = i+1; j < nTopics; j++) {
  1702.                     if (strcmp(topics[i], topics[j]) > 0) {
  1703.                         temp = topics[i];
  1704.                         topics[i] = topics[j];
  1705.                         topics[j] = temp;
  1706.                     }
  1707.                 }
  1708.             }
  1709.             HelpTopics = NewHandle(0);
  1710.             size = 0;
  1711.             for (item = 0; item < nTopics; item++) {
  1712.                 c2pstr(topics[item]);
  1713.                 len = *(topics[item]);
  1714.                 SetHandleSize(HelpTopics, size + len + 1);
  1715.                 HLock(HelpTopics);
  1716.                 utl_CopyPString(*HelpTopics + size, topics[item]);
  1717.                 HUnlock(HelpTopics);
  1718.                 size += len + 1;
  1719.             }
  1720.         }
  1721.         utl_CopyPString(HelpServer, server);
  1722.         GetDateTime(&LastHelpUpdate);
  1723.         help_BuildHelpMenu();
  1724.     }
  1725.     HUnlock(Response);
  1726.     return rCode;
  1727. }
  1728.  
  1729. /*_____________________________________________________________________
  1730.  
  1731.     GetSiteInfo - Get Site Info.
  1732.     
  1733.     Exit:        function result = error code.
  1734.                 SiteMenu = handle to site popup menu, or nil if error.
  1735.                 NumSites = number of sites in popup menu.
  1736.                 Servers = handle to list of server domain names.
  1737.                 Domains = handle to list of email domain names.
  1738.                 
  1739. query ns-servers type=serverlist return text
  1740. 102:There was 1 match to your request.
  1741. -200:1:    text: site:Northwestern University
  1742. -200:1:        : server:ns.nwu.edu
  1743. -200:1:        : domain:nwu.edu
  1744. -200:1:        : site:University of Arizona
  1745. -200:1:        : server:zippy.telcom.arizona.edu
  1746. -200:1:        : site:University of Florida
  1747. -200:1:        : server:ns.eng.ufl.edu
  1748. -200:1:        : site:University of Illinois at Urbana-Champaign
  1749. -200:1:        : server:ns.uiuc.edu
  1750. -200:1:        : domain:uiuc.edu
  1751. -200:1:        : site:University of Laval, Quebec, Canada
  1752. -200:1:        : server:ns.gel.ulaval.ca
  1753. -200:1:        : site:University of Minnesota
  1754. -200:1:        : server:ns.acs.umn.edu
  1755. -200:1:        : site:University of Waterloo, Ontario, Canada
  1756. -200:1:        : server:ns.waterloo.edu
  1757. -200:1:        : site:University of Wisconsin at Madison
  1758. -200:1:        : server:wisc.edu
  1759. -200:1:        : domain:wisc.edu
  1760. 200:Ok.
  1761.  
  1762. query ns-servers type=serverlist return text
  1763. 501:No matches to your query.
  1764. _____________________________________________________________________*/
  1765.  
  1766. static OSErr GetSiteInfo (void)
  1767.  
  1768. {
  1769.     Str255            cmd;                /* server command */
  1770.     OSErr                rCode;            /* error code */
  1771.     Str255            site;                /* site name */
  1772.     Str255            server;            /* server domain name */
  1773.     Str255            domain;            /* email domain name */
  1774.     Str255            siteStr;            /* "site" constant string */
  1775.     Str255            serverStr;        /* "server" constant string */
  1776.     Str255            domainStr;        /* "domain" constant string */
  1777.     char                *p;                /* pointer into response */
  1778.     char                *q;                /* pointer into response */
  1779.     char                *r;                /* pointer into response */
  1780.     short                code;                /* server response code */
  1781.     short                len1;                /* length of tag */
  1782.     short                len2;                /* length of string */
  1783.     long                serversSize;    /* size of Servers list */
  1784.     long                domainsSize;    /* size of Domains list */
  1785.  
  1786.     GetIndString(cmd, serverCmds, phSiteQuery);
  1787.     if (rCode = DoCommand(cmd)) return rCode;
  1788.     if (SiteMenu) DisposeMenu(SiteMenu);
  1789.     SiteMenu = nil;
  1790.     GetIndString(siteStr, stringsID, siteTag);
  1791.     GetIndString(serverStr, stringsID, serverTag);
  1792.     GetIndString(domainStr, stringsID, domainTag);
  1793.     SiteMenu = NewMenu(popupID, "\p");
  1794.     NumSites = 0;
  1795.     Servers = NewHandle(0);
  1796.     serversSize = 0;
  1797.     Domains = NewHandle(0);
  1798.     domainsSize = 0;
  1799.     *site = *server = *domain = 0;
  1800.     HLock(Response);
  1801.     p = *Response+1;
  1802.     while (true) {
  1803.         code = atoi(p);
  1804.         if (abs(code) == phSuccess) {
  1805.             if (code == -phSuccess) {
  1806.                 if (Skip(&p)) break;
  1807.                 if (Skip(&p)) break;
  1808.                 if (Skip(&p)) break;
  1809.                 r = strpbrk(p, ":\r");
  1810.                 if (!r || *r != ':') break;
  1811.                 len1 = r-p;
  1812.                 if (len1 > 255) len1 = 255;
  1813.                 r++;
  1814.                 q = strchr(r, '\r');
  1815.                 if (!q) break;
  1816.                 len2 = q-r;
  1817.                 if (len2 > 255) len2 = 255;
  1818.             }
  1819.             if (code == phSuccess ||
  1820.                 (len1 == *siteStr && !strncmp(p, siteStr+1, len1))) {
  1821.                 if (*site) {
  1822.                     NumSites++;
  1823.                     AppendMenu(SiteMenu, "\p ");
  1824.                     SetItem(SiteMenu, NumSites, site);
  1825.                     SetHandleSize(Servers, serversSize + *server + 1);
  1826.                     utl_CopyPString(*Servers + serversSize, server);
  1827.                     serversSize += *server + 1;
  1828.                     SetHandleSize(Domains, domainsSize + *domain + 1);
  1829.                     utl_CopyPString(*Domains + domainsSize, domain);
  1830.                     domainsSize += *domain + 1;
  1831.                 }
  1832.                 if (code == phSuccess) break;
  1833.                 *site = len2;
  1834.                 memcpy(site+1, r, len2);
  1835.                 *server = *domain = 0;
  1836.             } else if (len1 == *serverStr && !strncmp(p, serverStr+1, len1)) {
  1837.                 *server = len2;
  1838.                 memcpy(server+1, r, len2);
  1839.             } else if (len1 == *domainStr && !strncmp(p, domainStr+1, len1)) {
  1840.                 *domain = len2;
  1841.                 memcpy(domain+1, r, len2);
  1842.             } else {
  1843.                 break;
  1844.             }
  1845.         } else if (code == phMatchCount) {
  1846.             q = strchr(p, '\r');
  1847.             if (!q) break;
  1848.         } else {
  1849.             break;
  1850.         }
  1851.         p = q+1;
  1852.     }
  1853.     HUnlock(Response);
  1854.     if (NumSites) {
  1855.         CalcMenuSize(SiteMenu);
  1856.     } else {
  1857.         DisposeMenu(SiteMenu);
  1858.         SiteMenu = nil;
  1859.     }
  1860.     query_NewSiteList();
  1861.     GetDateTime(&LastSiteUpdate);
  1862.     return noErr;
  1863. }
  1864.  
  1865. /*_____________________________________________________________________
  1866.  
  1867.     CheckLists - Check If Time to Update Site and Help Lists.
  1868.     
  1869.     Entry:    server = Ph server host domain name.
  1870.     
  1871.     Exit:        function result = error code.
  1872. _____________________________________________________________________*/
  1873.  
  1874. OSErr CheckLists (Str255 server)
  1875.  
  1876. {
  1877.     unsigned long        now;            /* current time */
  1878.     OSErr                    rCode;        /* error code */
  1879.  
  1880.     GetDateTime(&now);
  1881.     if (EqualString(server, HelpServer, true, true)) {
  1882.         if (now - LastHelpUpdate > updateInterval) {
  1883.             if (rCode = GetHelpTopics(server)) return rCode;
  1884.         }
  1885.     }
  1886.     if (EqualString(server, DefaultServer, true, true)) {
  1887.         if (now - LastSiteUpdate > updateInterval) {
  1888.             if (rCode = GetSiteInfo()) return rCode;
  1889.         }
  1890.     }
  1891.     return noErr;
  1892. }
  1893.  
  1894. /*_____________________________________________________________________
  1895.  
  1896.     serv_Login - Login
  1897.     
  1898.     Entry:    server = Ph server host domain name.
  1899.                 loginUser = login alias or username.
  1900.                 pswd = login password.
  1901.                 
  1902.     Exit:        function result = error code.
  1903.                 loginAlias = login user alias.
  1904.                 fields = handle to initialized field info array.
  1905.                 numfields = number of fields.
  1906.                 sCode = server response code:
  1907.                     phSuccess = success.
  1908.                     phPermErr = login failed due to bad username or password.
  1909.                     phManyMatches = more than one match to username.
  1910.                     phFailReadOnly = database is read-only.
  1911.                     phFieldNotThere = record has no alias.
  1912.                     other = unexpected.
  1913.                 proxyList = handle to list of aliases for which the login
  1914.                     alias is a proxy, as a sequence of Pascal strings.
  1915.                 proxyCode = server response code to proxy query:
  1916.                     phSuccess = success.
  1917.                     phManyMatches = too many matches.
  1918.                     phNoAuthSearch = field does not have Lookup attribute.
  1919.                     phNoInxField = field does not have Indexed attribute.
  1920.                     other = unexpected.
  1921. _____________________________________________________________________*/
  1922.  
  1923. OSErr serv_Login (Str255 server, Str255 loginUser, Str255 pswd,
  1924.     Str255 loginAlias, FieldInfo ***fields, short *numFields,
  1925.     short *sCode, Handle *proxyList, short *proxyCode)
  1926.     
  1927. {
  1928.     OSErr                rCode;            /* result code */
  1929.     short                code;                /* server response code */
  1930.  
  1931.     tran_BeginTransaction(waitGetRecord, nil);
  1932.     rCode = OpenConnection(server);
  1933.     if (!rCode)
  1934.         rCode = CheckLists(server);
  1935.     if (!rCode) 
  1936.         rCode = GetFieldInfo(fields, numFields, &code);
  1937.     if (!rCode && code == phSuccess) 
  1938.         rCode = Login(loginUser, pswd, loginAlias, &code);
  1939.     if (!rCode && code == phSuccess) 
  1940.         rCode = GetFieldData(loginAlias, *fields, *numFields, &code);
  1941.     if (!rCode && code == phSuccess)
  1942.         rCode = GetProxyList(loginAlias, proxyList, proxyCode);
  1943.     *sCode = code;
  1944.     if (!rCode) rCode = Quit();
  1945.     if (rCode) {
  1946.         AbortConnection();
  1947.     } else {
  1948.         rCode = CloseConnection();
  1949.     }
  1950.     tran_EndTransaction();
  1951.     return rCode;
  1952. }
  1953.  
  1954. /*_____________________________________________________________________
  1955.  
  1956.     serv_GetRecord - Get Ph Record
  1957.     
  1958.     Entry:    server = Ph server host domain name.
  1959.                 loginUser = login alias.
  1960.                 pswd = login password.
  1961.                 user = alias or username of record to get from server.
  1962.                 
  1963.     Exit:        function result = error code.
  1964.                 fields = handle to initialized field info array.
  1965.                 numfields = number of fields.
  1966.                 canPut = true if login alias has permission to make changes
  1967.                     to the user alias record.
  1968.                 sCode = server response code:
  1969.                     phSuccess = success.
  1970.                     phNoMatches = no matches to username.
  1971.                     phManyMatches = more than one match to username.
  1972.                     other = unexpected.
  1973. _____________________________________________________________________*/
  1974.  
  1975. OSErr serv_GetRecord (Str255 server, Str255 loginUser, Str255 pswd, Str255 user,
  1976.     FieldInfo ***fields, short *numFields, short *sCode)
  1977.     
  1978. {
  1979.     OSErr                rCode;            /* result code */
  1980.     short                code;                /* server response code */
  1981.     Str255            junk;                /* junk */
  1982.  
  1983.     tran_BeginTransaction(waitGetRecord, nil);
  1984.     rCode = OpenConnection(server);
  1985.     if (!rCode)
  1986.         rCode = CheckLists(server);
  1987.     if (!rCode) 
  1988.         rCode = GetFieldInfo(fields, numFields, &code);
  1989.     if (!rCode && code == phSuccess) 
  1990.         rCode = Login(loginUser, pswd, junk, &code);
  1991.     if (!rCode && code == phSuccess) 
  1992.         rCode = GetFieldData(user, *fields, *numFields, &code);
  1993.     *sCode = code;
  1994.     if (!rCode) rCode = Quit();
  1995.     if (rCode) {
  1996.         AbortConnection();
  1997.     } else {
  1998.         rCode = CloseConnection();
  1999.     }
  2000.     tran_EndTransaction();
  2001.     return rCode;
  2002. }
  2003.  
  2004. /*_____________________________________________________________________
  2005.  
  2006.     serv_PutRecord - Put Ph Record
  2007.     
  2008.     Entry:    server = Ph server host domain name.
  2009.                 loginAlias = login alias.
  2010.                 pswd = login password.
  2011.                 alias = alias of record to update.
  2012.                 fields = handle to field info array.
  2013.                 numFields = number of fields.
  2014.                 
  2015.     Exit:        function result = error code.
  2016.                 sCode = server response code:
  2017.                     phSuccess = success.
  2018.                     phFailReadOnly = database is read-only.
  2019.                     other = unexpected.
  2020. _____________________________________________________________________*/
  2021.  
  2022. OSErr serv_PutRecord (Str255 server, Str255 loginAlias, Str255 pswd,
  2023.     Str255 alias, FieldInfo **fields, short numFields, short *sCode)
  2024.     
  2025. {
  2026.     OSErr                rCode;            /* result code */
  2027.     Str255            junk;                /* junk */
  2028.     short                code;                /* server response code */
  2029.  
  2030.     tran_BeginTransaction(waitPutRecord, nil);
  2031.     rCode = OpenConnection(server);
  2032.     if (!rCode)
  2033.         rCode = CheckLists(server);
  2034.     if (!rCode) 
  2035.         rCode = Login(loginAlias, pswd, junk, &code);
  2036.     if (!rCode && code == phSuccess) 
  2037.         rCode = PutFieldData(alias, fields, numFields);
  2038.     *sCode = code;
  2039.     if (!rCode) rCode = Quit();
  2040.     if (rCode) {
  2041.         AbortConnection();
  2042.     } else {
  2043.         rCode = CloseConnection();
  2044.     }
  2045.     tran_EndTransaction();
  2046.     return rCode;
  2047. }
  2048.  
  2049. /*_____________________________________________________________________
  2050.  
  2051.     serv_ChangePassword - Change Ph Password.
  2052.     
  2053.     Entry:    server = Ph server host domain name.
  2054.                 loginAlias = login alias.
  2055.                 pswd = login password.
  2056.                 alias = alias of record to change.
  2057.                 newPswd = new password.
  2058.                 
  2059.     Exit:        function result = error code.
  2060.                 sCode = server response code:
  2061.                     phSuccess = success.
  2062.                     phFailReadOnly = database is read-only.
  2063.                     phIllegalVal = illegal value.
  2064.                     other = unexpected.
  2065.                 servErrMsg = server error message if sCode != phSuccess,
  2066.                     or empty string if none.
  2067. _____________________________________________________________________*/
  2068.  
  2069. OSErr serv_ChangePassword (Str255 server, Str255 loginAlias, Str255 pswd, 
  2070.     Str255 alias, Str255 newPswd, short *sCode, Str255 servErrMsg)
  2071.  
  2072. {
  2073.     OSErr                rCode;            /* result code */
  2074.     Str255            junk;                /* junk */
  2075.     short                code;                /* server response code */
  2076.  
  2077.     *servErrMsg = 0;
  2078.     tran_BeginTransaction(waitChangePswd, nil);
  2079.     rCode = OpenConnection(server);
  2080.     if (!rCode)
  2081.         rCode = CheckLists(server);
  2082.     if (!rCode) 
  2083.         rCode = Login(loginAlias, pswd, junk, &code);
  2084.     if (!rCode && code == phSuccess) 
  2085.         rCode = ChangePassword(alias, newPswd, &code, servErrMsg);
  2086.     *sCode = code;
  2087.     if (!rCode) rCode = Quit();
  2088.     if (rCode) {
  2089.         AbortConnection();
  2090.     } else {
  2091.         rCode = CloseConnection();
  2092.     }
  2093.     tran_EndTransaction();
  2094.     return rCode;
  2095. }
  2096.  
  2097. /*_____________________________________________________________________
  2098.  
  2099.     serv_CreateRecord - Create Ph Record.
  2100.     
  2101.     Entry:    server = Ph server host domain name.
  2102.                 loginAlias = login alias.
  2103.                 loginPswd = login password.
  2104.                 alias = alias for new record.
  2105.                 name = name for new record.
  2106.                 type = type of new record.
  2107.                 pswd = password for new record.
  2108.                 
  2109.     Exit:        function result = server response code.
  2110.                 fields = handle to initialized field info array.
  2111.                 numfields = number of fields.
  2112.                 sCode = server response code:
  2113.                     phSuccess = successful.
  2114.                     phFailReadOnly = database is read-only.
  2115.                     phDupAlias = duplicate alias.
  2116.                     phIllegalVal = illegal value.
  2117.                     other = unexpected.
  2118.                 whichField = illegal field number if sCode != phSuccess:
  2119.                     -1 = no field.
  2120.                     0 = alias.
  2121.                     1 = name.
  2122.                     2 = type.
  2123.                     3 = password.
  2124.                 servErrMsg = server error message text if sCode != phSuccess
  2125.                     and whichField >= 0.
  2126. _____________________________________________________________________*/
  2127.  
  2128. OSErr serv_CreateRecord (Str255 server, Str255 loginAlias, 
  2129.     Str255 loginPswd, Str255 alias, Str255 name, Str255 type, Str255 pswd, 
  2130.     FieldInfo ***fields, short *numFields, short *sCode, short *whichField,
  2131.     Str255 servErrMsg)
  2132.     
  2133. {
  2134.     OSErr                rCode;            /* result code */
  2135.     Str255            junk;                /* junk */
  2136.     short                code;                /* server response code */
  2137.     
  2138.     tran_BeginTransaction(waitNewRecord, nil);
  2139.     *whichField = -1;
  2140.     rCode = OpenConnection(server);
  2141.     if (!rCode)
  2142.         rCode = CheckLists(server);
  2143.     if (!rCode) 
  2144.         rCode = Login(loginAlias, loginPswd, junk, &code);
  2145.     if (!rCode && code == phSuccess) {
  2146.         *whichField = 0;
  2147.         rCode = CreateRecord(alias, &code, servErrMsg);
  2148.     }
  2149.     if (!rCode && code == phSuccess)
  2150.         rCode = CreateNewFields(alias, name, type, &code, whichField, servErrMsg);
  2151.     if (!rCode && code == phSuccess && *pswd) {
  2152.         *whichField = 3;
  2153.         rCode = ChangePassword(alias, pswd, &code, servErrMsg);
  2154.     }
  2155.     if (!rCode && code == phSuccess) {
  2156.         *whichField = -1;
  2157.         rCode = GetFieldInfo(fields, numFields, &code);
  2158.     }
  2159.     if (!rCode && code == phSuccess) 
  2160.         rCode = GetFieldData(alias, *fields, *numFields, &code);
  2161.     *sCode = code;
  2162.     if (!rCode && code != phSuccess && *whichField > 0)
  2163.         DeleteRecord(alias, &code);
  2164.     if (!rCode) rCode = Quit();
  2165.     if (rCode) {
  2166.         AbortConnection();
  2167.     } else {
  2168.         rCode = CloseConnection();
  2169.     }
  2170.     tran_EndTransaction();
  2171.     return rCode;
  2172. }
  2173.  
  2174. /*_____________________________________________________________________
  2175.  
  2176.     serv_DeleteRecord - Delete Ph Record.
  2177.     
  2178.     Entry:    server = Ph server host domain name.
  2179.                 loginAlias = login alias.
  2180.                 loginPswd = login password.
  2181.                 alias = alias of record to be deleted.
  2182.                 
  2183.     Exit:        function result = error code.
  2184.                 sCode = server response code:
  2185.                     phSuccess = success.
  2186.                     phNoMatches = no matches to alias.
  2187.                     phFailReadOnly = database is read-only.
  2188.                     other = unexpected.
  2189. _____________________________________________________________________*/
  2190.  
  2191. OSErr serv_DeleteRecord (Str255 server, Str255 loginAlias,
  2192.     Str255 loginPswd, Str255 alias, short *sCode)
  2193.     
  2194. {
  2195.     OSErr                rCode;            /* result code */
  2196.     Str255            junk;                /* junk */
  2197.     short                code;                /* server response code */
  2198.     
  2199.     tran_BeginTransaction(waitDelRecord, nil);
  2200.     rCode = OpenConnection(server);
  2201.     if (!rCode)
  2202.         rCode = CheckLists(server);
  2203.     if (!rCode) 
  2204.         rCode = Login(loginAlias, loginPswd, junk, &code);
  2205.     if (!rCode && code == phSuccess) rCode = DeleteRecord(alias, &code);
  2206.     *sCode = code;
  2207.     if (!rCode) rCode = Quit();
  2208.     if (rCode) {
  2209.         AbortConnection();
  2210.     } else {
  2211.         rCode = CloseConnection();
  2212.     }
  2213.     tran_EndTransaction();
  2214.     return rCode;
  2215. }
  2216.  
  2217. /*_____________________________________________________________________
  2218.  
  2219.     serv_DoQuery - Do a Server Query.
  2220.     
  2221.     Entry:    server = Ph server host domain name.
  2222.                 query = handle to query.
  2223.                 queryLen = length of query.
  2224.                 emailDomain = email domain name, empty string if none.
  2225.                 reply = preallocated reply handle.
  2226.                 
  2227.     Exit:        function result = error code.
  2228.                 reply = handle to reply, size adjusted to exactly hold
  2229.                     the reply.
  2230.                 
  2231.                 If server errors occur, the server error messages are
  2232.                 placed in the reply. The match count message, if any,
  2233.                 is also placed in the reply.
  2234. _____________________________________________________________________*/
  2235.  
  2236. OSErr serv_DoQuery (Str255 server, Handle query,
  2237.     unsigned short queryLen, Str255 emailDomain, Handle reply)
  2238.     
  2239. {
  2240.     OSErr            rCode;            /* result code */
  2241.     
  2242.     tran_BeginTransaction(waitQuery, nil);
  2243.     rCode = OpenConnection(server);
  2244.     if (!rCode)
  2245.         rCode = CheckLists(server);
  2246.     if (!rCode)
  2247.         rCode = SendQuery(query, queryLen, emailDomain, reply);
  2248.     if (!rCode) rCode = Quit();
  2249.     if (rCode) {
  2250.         AbortConnection();
  2251.     } else {
  2252.         rCode = CloseConnection();
  2253.     }
  2254.     tran_EndTransaction();
  2255.     return rCode;
  2256. }
  2257.  
  2258. /*_____________________________________________________________________
  2259.  
  2260.     serv_GetHelp - Get Server Help Text.
  2261.     
  2262.     Entry:    server = Ph server host domain name.
  2263.                 topic = help topic.
  2264.                 reply = preallocated reply handle.
  2265.                 
  2266.     Exit:        function result = error code.
  2267.                 reply = handle to reply, size adjusted to exactly hold
  2268.                     the reply.
  2269.                 
  2270.                 If server errors occur, the server error messages are
  2271.                 placed in the reply.
  2272. _____________________________________________________________________*/
  2273.  
  2274. OSErr serv_GetHelp (Str255 server, Str255 topic, Handle reply)
  2275.     
  2276. {
  2277.     OSErr            rCode;            /* result code */
  2278.     
  2279.     tran_BeginTransaction(waitHelp, nil);
  2280.     rCode = OpenConnection(server);
  2281.     if (!rCode)
  2282.         rCode = CheckLists(server);
  2283.     if (!rCode)
  2284.         rCode = GetHelp(topic, reply);
  2285.     if (!rCode) rCode = Quit();
  2286.     if (rCode) {
  2287.         AbortConnection();
  2288.     } else {
  2289.         rCode = CloseConnection();
  2290.     }
  2291.     tran_EndTransaction();
  2292.     return rCode;
  2293. }
  2294.  
  2295. /*_____________________________________________________________________
  2296.  
  2297.     serv_GetReadOnlyReason - Get Read Only Reason
  2298.                 
  2299.     Exit:        reason = read only reason.
  2300. _____________________________________________________________________*/
  2301.  
  2302. void serv_GetReadOnlyReason (Str255 reason)
  2303.  
  2304. {
  2305.     utl_CopyPString(reason, ReadOnlyReason);
  2306. }
  2307.  
  2308. /*_____________________________________________________________________
  2309.  
  2310.     serv_GetSiteList - Get Site List.
  2311.     
  2312.     Entry:    server = Ph server host domain name.
  2313.                 
  2314.     Exit:        function result = error code.
  2315. _____________________________________________________________________*/
  2316.  
  2317. OSErr serv_GetSiteList (Str255 server)
  2318.  
  2319. {
  2320.     OSErr            rCode;            /* result code */
  2321.     
  2322.     tran_BeginTransaction(waitSiteList, server);
  2323.     rCode = OpenConnection(server);
  2324.     if (!rCode)
  2325.         rCode = GetSiteInfo();
  2326.     if (!rCode) rCode = Quit();
  2327.     if (rCode) {
  2328.         AbortConnection();
  2329.     } else {
  2330.         rCode = CloseConnection();
  2331.     }
  2332.     tran_EndTransaction();
  2333.     return rCode;
  2334. }
  2335.  
  2336. /*_____________________________________________________________________
  2337.  
  2338.     serv_GetHelpList - Get Help Topic List.
  2339.     
  2340.     Entry:    server = Ph server host domain name.
  2341.                 
  2342.     Exit:        function result = error code.
  2343. _____________________________________________________________________*/
  2344.  
  2345. OSErr serv_GetHelpList (Str255 server)
  2346.  
  2347. {
  2348.     OSErr            rCode;            /* result code */
  2349.     
  2350.     tran_BeginTransaction(waitHelpList, server);
  2351.     rCode = OpenConnection(server);
  2352.     if (!rCode)
  2353.         rCode = GetHelpTopics(server);
  2354.     if (!rCode) rCode = Quit();
  2355.     if (rCode) {
  2356.         AbortConnection();
  2357.     } else {
  2358.         rCode = CloseConnection();
  2359.     }
  2360.     tran_EndTransaction();
  2361.     return rCode;
  2362. }
  2363.