home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / XGRP_000.SZH / NMAILOUT.C < prev    next >
C/C++ Source or Header  |  1991-08-22  |  18KB  |  606 lines

  1. /* netmail scanner */
  2.  
  3. #include "xgroup.h"
  4.  
  5. #define MAXKLUDGE 256
  6.  
  7. #define SETSCANNED(x) (x.m_attr |= MSGSCANNED)
  8. #define SETSENT(x) (x.attr |= MSGSENT)
  9. #define RESETSENT(x) (x.attr &= (~MSGSENT))
  10. #define SETDELETED(x) (x.m_attr |= MSGDELETED)
  11.  
  12. /* function declarations specific to this module */
  13.  
  14. static int  _fastcall open_pkt (ADDR *addr);
  15. static word _fastcall find_last_scanned (int dataptr);
  16. static int  _fastcall write_pkt_msg (int fp,XMSG *amsg,char *text);
  17. static void _fastcall report(void);
  18. static int  _fastcall do_export(word areano,word startat,int dataptr,
  19.                                 int textptr);
  20. static int _fastcall do_attach_or_request (XMSG *amsg,ADDR *addr);
  21.  
  22. /* external var references */
  23.  
  24. extern GROUP    *group;
  25. extern CONTROLS control;
  26. extern ADDR     *myaddr;
  27. extern ECHOREC  *echos;
  28. extern char     *groupin;
  29. extern char     *groupout;
  30. extern char     *grouphold;
  31. extern char     *msgdir;
  32. extern char     *archive;
  33. extern char     *unarchive;
  34. extern char     *outbound;
  35. extern char     *inbound;
  36. extern word     packsize;
  37. extern char     buffer[1024];
  38. extern word     netarea;
  39.  
  40.  
  41.  
  42.  
  43.  
  44. void _fastcall net_export_mail (void) {
  45.  
  46.     word        y,totalmsgs;
  47.     int         dataptr,textptr;
  48.     struct stat st;
  49.  
  50.  
  51.     printf("\nScanning net area #%u...",netarea);
  52.     sprintf(buffer,"%s/XDATA.%03x",msgdir,netarea);
  53.     if(stat(buffer,&st)) st.st_size = 0L;
  54.     if(!st.st_size) {
  55.         printf("no msgs\n");
  56.         my_unlink(buffer);
  57.         sprintf(buffer,"%s/XTEXT.%03x",msgdir,netarea);
  58.         my_unlink(buffer);
  59.         return;
  60.     }
  61.     else printf("%lu msg%s\n",st.st_size / (long)sizeof(XMSG),
  62.                 &"s"[((st.st_size / (long)sizeof(XMSG)) == 1L)]);
  63.     dataptr = sopen(buffer,O_RDWR | O_BINARY,SH_DENYNO);
  64.     if(dataptr == -1) {
  65.         printf("\r\nCouldn't open data file\r\n");
  66.         return;
  67.     }
  68.     sprintf(buffer,"%s/XTEXT.%03x",msgdir,netarea);
  69.     textptr = sopen(buffer,O_RDWR | O_BINARY,SH_DENYNO);
  70.     if(textptr == -1) {
  71.         close(dataptr);
  72.         printf("\r\nCouldn't open text file\r\n");
  73.         return;
  74.     }
  75.     y = find_last_scanned(dataptr);
  76.     totalmsgs = do_export(netarea,y,dataptr,textptr);
  77.     close(textptr);
  78.     close(dataptr);
  79.  
  80.     printf("\n");
  81.  
  82.     if(totalmsgs)
  83.             logf("Exported %u msg%s from net area #%u",totalmsgs,
  84.                      &"s"[totalmsgs == 1],netarea);
  85. }
  86.  
  87.  
  88.  
  89. static int _fastcall do_export (word areano,word startat,int dataptr,
  90.                                 int textptr) {
  91.  
  92.     word          x,nummsgs,totalmsgs = 0,tries,test;
  93.     long          pos;
  94.     int           fp;
  95.     char          *text = NULL,*p;
  96.     XMSG          xmsg;
  97.     ADDR          addr;
  98.  
  99.  
  100.     addr.domain = NULL;
  101.  
  102.     tries = 0;
  103.     while(tries < 50) {
  104.         if(lseek(dataptr,0L,SEEK_END) != -1L) break;
  105.         DosSleep(100L);
  106.         tries++;
  107.     }
  108.     if(tries >= 50) {
  109.         printf("\nLocking conflict; aborting...(#4)\n");
  110.         return totalmsgs;
  111.     }
  112.     nummsgs = (word)(tell(dataptr) / (long)sizeof(XMSG));
  113.     if(startat) startat--;
  114.     for(x = startat;x < nummsgs;x++) {
  115.  
  116.         tries = 0;
  117.         while(tries < 50) {
  118.             if(lseek(dataptr,(long)x * (long)sizeof(XMSG),SEEK_SET) != -1L) break;
  119.             DosSleep(100L);
  120.             tries++;
  121.         }
  122.         if(tries >= 50) {
  123.             printf("\nLocking conflict; aborting...(#5)\n");
  124.             return totalmsgs;
  125.         }
  126.  
  127.         pos = tell(dataptr);
  128.  
  129.         tries = 0;
  130.         while(tries < 50) {
  131.             if((size_t)read(dataptr,&xmsg,sizeof(XMSG)) == sizeof(XMSG)) break;
  132.             DosSleep(100L);
  133.             tries++;
  134.             lseek(dataptr,pos,SEEK_SET);
  135.         }
  136.         if(tries >= 50) {
  137.             printf("\nLocking conflict; aborting...(#6)\n");
  138.             return totalmsgs;
  139.         }
  140.  
  141.         if(xmsg.m_attr & MSGSCANNED) continue;
  142.         if(xmsg.m_attr & MSGDELETED) goto ExportDone;
  143.         if(!(xmsg.m_attr & MSGNET)) goto ExportDone;
  144.         if(!(xmsg.attr & MSGLOCAL)) goto ExportDone;
  145.         if(xmsg.attr & MSGORPHAN) goto ExportDone;
  146.  
  147.         /* Got one to export */
  148.  
  149.         RESETSENT(xmsg);   /* Reset sent bit--what the hell, be nice */
  150.  
  151.         tries = 0;
  152.         while(tries < 50) {
  153.             if(lseek(textptr,xmsg.start,SEEK_SET) != -1L) break;
  154.             DosSleep(100L);
  155.             tries++;
  156.         }
  157.         if(tries >= 50) {
  158.             printf("\nLocking conflict; aborting...(#7)\n");
  159.             return totalmsgs;
  160.         }
  161.  
  162.         text = (char *)malloc(xmsg.length + 2);
  163.         if(!text) {
  164.             printf("\nOut of memory\n");
  165.             continue;
  166.         }
  167.  
  168.         tries = 0;
  169.         while(tries < 50) {
  170.             *text = 0;
  171.             test = (word)read(textptr,text,xmsg.length + 1);
  172.             if(test != 65535U) {
  173.                 if(test >= (xmsg.length - 1)) break;
  174.             }
  175.             DosSleep(100L);
  176.             tries++;
  177.             lseek(textptr,xmsg.start,SEEK_SET);
  178.         }
  179.         if(tries >= 50) {
  180.             printf("\nLocking conflict; aborting...(#8)\n");
  181.             return totalmsgs;
  182.         }
  183.  
  184.         if(!*text){
  185.             if(text) my_free(text);
  186.             text = NULL;
  187.             printf("\nDidn't get any text\n");
  188.             continue;
  189.         }
  190.  
  191.         if(xmsg.m_attr & MSGPACKED) {
  192.             if(unpack_msg(&text) == NULL) {
  193.                 if(text) my_free(text);
  194.                 text = NULL;
  195.                 continue;
  196.             }
  197.         }
  198.  
  199.         addr.zone   = xmsg.d_zone;
  200.         addr.net    = xmsg.dest_net;
  201.         addr.node   = xmsg.dest;
  202.         addr.point  = xmsg.d_point;
  203.         addr.domain = NULL;
  204.         addr.next = NULL;
  205.  
  206.         if((p = strstr(text,"\01MSGTO: "))) {
  207.             if((p - 1) < text || *(p - 1) == '\r') {
  208.                 p += 8;
  209.                 p = skip_white(p);
  210.                 parse_addr(&p,&addr,myaddr);
  211.             }
  212.             else guess_rest(&addr,myaddr);
  213.         }
  214.         else guess_rest(&addr,myaddr);
  215.  
  216.         if((xmsg.attr & MSGFILE) || (xmsg.attr & MSGFRQ) || (xmsg.attr & MSGURQ)) {
  217.             do_attach_or_request(&xmsg,&addr);
  218.         }
  219.  
  220.         fp = open_pkt(&addr);
  221.         if(fp == -1) {
  222.             if(text) my_free(text);
  223.             if(addr.domain) my_free(addr.domain);
  224.             addr.domain = NULL;
  225.             text = NULL;
  226.             continue;
  227.         }
  228.  
  229.         if(addr.zone == myaddr->zone && addr.point == myaddr->point &&
  230.           addr.net == myaddr->net && addr.node == myaddr->node &&
  231.           (!addr.domain || !stricmp(addr.domain,myaddr->domain))) {
  232.             logf("Netmail msg #%u skipped; seems to be misaddressed",
  233.                  x + 1);
  234.             my_free(text);
  235.             my_free(addr.domain);
  236.             addr.domain = NULL;
  237.             text = NULL;
  238.             close(fp);
  239.             continue;
  240.         }
  241.  
  242.  
  243.         printf("\x1b[K#%u (#%u) -> %u:%u/%u.%u@%s\r",
  244.                x+1,++totalmsgs,addr.zone,addr.net,addr.node,addr.point,
  245.                addr.domain);
  246.  
  247.         logf("Exported netmail msg #%u -> %u:%u/%u.%u@%s",
  248.                x+1,addr.zone,addr.net,addr.node,addr.point,
  249.                addr.domain);
  250.  
  251.  
  252.         my_free(addr.domain);
  253.         addr.domain = NULL;
  254.         text[xmsg.length] = 0;
  255.         text[xmsg.length-1] = 0;
  256.         write_pkt_msg(fp,&xmsg,text);
  257.         close(fp);
  258.         my_free(text);
  259.         text = NULL;
  260.         SETSENT(xmsg);      /* Set sent bit */
  261.         if(xmsg.attr & MSGKILL) SETDELETED(xmsg); /* delete if so marked*/
  262.  
  263. ExportDone:
  264.  
  265.         SETSCANNED(xmsg);   /* Rewrite header w/ echos-scanned bit set */
  266.         tries = 0;
  267.         while(tries < 50) {
  268.             if(lseek(dataptr,pos,SEEK_SET) != -1L) break;
  269.             DosSleep(100L);
  270.             tries++;
  271.         }
  272.         if(tries < 50) {
  273.             locking(dataptr,LK_LOCK,(long)sizeof(XMSG));
  274.             write(dataptr,&xmsg,sizeof(XMSG));
  275.             lseek(dataptr,pos,SEEK_SET);
  276.             locking(dataptr,LK_UNLCK,(long)sizeof(XMSG));
  277.         }
  278.     }
  279.  
  280.     return totalmsgs;
  281. }
  282.  
  283.  
  284.  
  285. static int _fastcall open_pkt (ADDR *addr) {
  286.  
  287.     int         fp;
  288.     long        pos;
  289.     PKTHDR      ph;
  290.     ADDR        *maddr;
  291.     static char currout[257];
  292.     char        *p;
  293.  
  294.  
  295.     /* Open (create if necessary) a packet for address given.
  296.        Return a file handle for the packet positioned to eop */
  297.  
  298.     if(!control.xbbsos2) {
  299.         strcpy(currout,outbound);
  300.         if(addr->domain && *addr->domain && stricmp(myaddr->domain,addr->domain)) {
  301.                         p = strrchr(currout,'\\');
  302.             if(p) p++;
  303.             else p = &currout[strlen(currout)];
  304.             strcat(currout,addr->domain);
  305.         }
  306.         if(myaddr->zone != addr->zone) {
  307.              sprintf(&currout[strlen(currout)],".%03x",addr->zone);
  308.              DosMkDir(currout,0L);
  309.         }
  310.         sprintf(buffer,"%s/%04x%04x.OUT",currout,addr->net,addr->node);
  311.     }
  312.     else {
  313.         sprintf(buffer,"%s/P.%u.%u.%u.%u.%0.8s",outbound,addr->zone,addr->net,
  314.                 addr->node,addr->point,addr->domain);
  315.     }
  316.  
  317.     fp = sopen(buffer,O_RDWR | O_BINARY | O_CREAT,SH_DENYWR,S_IREAD | S_IWRITE);
  318.  
  319.     if(fp != -1) {
  320.         lseek(fp,0L,SEEK_END);
  321.         if(!tell(fp)) {
  322.             memset(&ph,0,sizeof(PKTHDR));
  323.             ph.version = 2;
  324.             ph.subver = 2;
  325.             ph.dnode  = addr->node;
  326.             ph.dnet   = addr->net;
  327.             ph.dzone  = addr->zone;
  328.             ph.dpoint = addr->point;
  329.             if(addr->domain) strncpy(ph.ddomain,addr->domain,8);
  330.             maddr = best_guess(addr,myaddr);
  331.             ph.onode  = maddr->node;
  332.             ph.onet   = maddr->net;
  333.             ph.ozone  = maddr->zone;
  334.             ph.opoint = maddr->point;
  335.             strncpy(ph.odomain,maddr->domain,8);
  336.             write(fp,&ph,sizeof(PKTHDR));
  337.             write(fp,"\0",2);
  338.         }
  339.         pos = tell(fp);
  340.         if(pos) lseek(fp,pos-2L,SEEK_SET);  /* Position to next msg spot */
  341.     }
  342.     else printf("\nCouldn't open packet \"%s\"\n",buffer);
  343.  
  344.     return fp;
  345. }
  346.  
  347.  
  348.  
  349.  
  350. static int _fastcall write_pkt_msg (int fp,XMSG *amsg,char *text) {
  351.  
  352.     char pmsg[192],*p;
  353.     word x;
  354.  
  355.  
  356.     /* Write the message given to the end of the file given as a
  357.        packed msg */
  358.  
  359.     memset(pmsg,192,0);
  360.     *pmsg=0x02;
  361.     pmsg[1]=0x00;
  362.     memcpy(&pmsg[2],&amsg->orig,2);
  363.     memcpy(&pmsg[4],&amsg->dest,2);
  364.     memcpy(&pmsg[6],&amsg->orig_net,2);
  365.     memcpy(&pmsg[8],&amsg->dest_net,2);
  366.     memcpy(&pmsg[10],&amsg->attr,2);
  367.     x = 0;
  368.     memcpy(&pmsg[12],&x,2);
  369.     for(x = 0;x < 20;x++) if(!amsg->date[x]) amsg->date[x]='Q';
  370.     amsg->date[19] = 0;                   /* Goddamn qmail anyway... */
  371.     memcpy(&pmsg[14],amsg->date,20);    
  372.     p = &pmsg[34];
  373.     strcpy(p,amsg->to);
  374.     x = 34;
  375.     while(*p){
  376.         x++;
  377.         p++;
  378.     }
  379.     p++;
  380.     x++;
  381.     strcpy(p,amsg->from);
  382.     while(*p){
  383.         x++;
  384.         p++;
  385.     }
  386.     x++;
  387.     p++;
  388.     strcpy(p,amsg->subj);
  389.     while(*p){
  390.         x++;
  391.         p++;
  392.     }
  393.     x++;
  394.     p++;
  395.     write(fp,pmsg,x);
  396.     write(fp,text,strlen(text));
  397.     write(fp,"\0\0",3);
  398.     lseek(fp,(tell(fp) - 2L),SEEK_SET);  /* Ready for another msg */
  399.     return 1;
  400. }
  401.  
  402.  
  403.  
  404. static word _fastcall find_last_scanned (int dataptr) { /* Speed up someday... */
  405.  
  406.     long          pos;
  407.     register word x;
  408.     int           tries;
  409.     XMSG          xmsg;
  410.  
  411.  
  412.  
  413.     tries = 0;
  414.     while(tries < 50) {
  415.         if(lseek(dataptr,0L,SEEK_END) != -1L) break;
  416.         DosSleep(100L);
  417.         tries++;
  418.     }
  419.     if(tries >= 50) {
  420.         printf("\nLocking conflict; aborting... (#1)\n");
  421.         return 0;
  422.     }
  423.  
  424.     pos = tell(dataptr);
  425.     x = (word)((pos / (long)sizeof(XMSG)) - 1L);
  426.     for(;x;x--) {
  427.  
  428.         tries = 0;
  429.         while(tries < 50) {
  430.             if(lseek(dataptr,(long)x * (long)sizeof(XMSG),SEEK_SET) != -1L) break;
  431.             DosSleep(100L);
  432.             tries++;
  433.         }
  434.         if(tries >= 50) {
  435.             printf("Locking contention...(#2)\n");
  436.             continue;
  437.         }
  438.  
  439.         tries = 0;
  440.         while(tries < 50) {
  441.             if((size_t)read(dataptr,&xmsg,sizeof(XMSG)) == sizeof(XMSG)) break;
  442.             DosSleep(100L);
  443.             tries++;
  444.             lseek(dataptr,(long)x * (long)sizeof(XMSG),SEEK_SET);
  445.         }
  446.         if(tries >= 50) {
  447.             printf("Locking contention...(#3)\n");
  448.             continue;
  449.         }
  450.  
  451.         if(xmsg.m_attr & MSGSCANNED) break;
  452.     }
  453.     return x;
  454. }
  455.  
  456.  
  457.  
  458. static int _fastcall do_attach_or_request (XMSG *amsg,ADDR *addr) {
  459.  
  460.     int         req = -1,search_handle,num_matches;
  461.     char        namebuf[257],*p,*pp,newsubj[70] = "",flavor = 'N',tempbuf[516];
  462.     struct stat st;
  463.     FILEFINDBUF f;
  464.     static char currout[257];
  465.  
  466.  
  467.     if(amsg->attr & MSGFRQ) {           /* Request */
  468.         amsg->attr &= (~MSGFRQ);
  469.         if(!control.xbbsos2) {
  470.             strcpy(currout,outbound);
  471.             if(addr->domain && *addr->domain && stricmp(myaddr->domain,addr->domain)) {
  472.                                 p = strrchr(currout,'\\');
  473.                 if(p) p++;
  474.                 else p = &currout[strlen(currout)];
  475.                 strcat(currout,addr->domain);
  476.             }
  477.             if(myaddr->zone != addr->zone) {
  478.                 sprintf(&currout[strlen(currout)],".%03x",addr->zone);
  479.                 DosMkDir(currout,0L);
  480.             }
  481.             sprintf(namebuf,"%s/%04x%04x.OUT",currout,addr->net,addr->node);
  482.         }
  483.         else {
  484.             sprintf(namebuf,"%s/R.%u.%u.%u.%u.%s",outbound,addr->zone,
  485.                     addr->net,addr->node,addr->point,addr->domain);
  486.         }
  487.         req = sopen(namebuf,O_CREAT | O_RDWR | O_BINARY,SH_DENYWR,S_IWRITE);
  488.         if(req == -1) return -1;
  489.         lseek(req,0L,SEEK_END);
  490.         strcpy(namebuf,amsg->subj);
  491.         lstrip(namebuf);
  492.         rstrip(namebuf);
  493.         p = strtok(namebuf," ");
  494.         while(p) {
  495.             ffprintf(req,"%s\r\n",p);
  496.             p = strtok(0," ");
  497.             if(p) lstrip(p);
  498.         }
  499.         close(req);
  500.     }
  501.     else if(amsg->attr & MSGFILE) {     /* Attach */
  502.         amsg->attr &= (~MSGFILE);
  503.         if(!control.xbbsos2) {
  504.             sprintf(namebuf,"%s/%04x%04x.FLO",currout,addr->net,addr->node);
  505.             if(amsg->m_attr & MSGHOLD) namebuf[strlen(namebuf)-3]='H';
  506.             else if(amsg->attr & MSGCRASH) namebuf[strlen(namebuf)-3]='C';
  507.         }
  508.         else {
  509.             if(amsg->m_attr & MSGHOLD) flavor = 'H';
  510.             else if(amsg->attr & MSGCRASH) flavor = 'C';
  511.             sprintf(namebuf,"%s/%c.%u.%u.%u.%u.%s",outbound,flavor,addr->zone,
  512.                     addr->net,addr->node,addr->point,addr->domain);
  513.         }
  514.         req = sopen(namebuf,O_RDWR | O_BINARY | O_CREAT,SH_DENYWR,S_IWRITE);
  515.         if(req == -1) return -1;
  516.         lseek(req,0L,SEEK_END);
  517.         strcpy(namebuf,amsg->subj);
  518.         lstrip(namebuf);
  519.         rstrip(namebuf);
  520.         p = strtok(namebuf," ");
  521.         while(p && *p) {
  522.             while(pp = strchr(p,'\\')) *pp = '/';
  523.             pp = strrchr(p,'/');
  524.             if(!pp) pp = strrchr(p,':');
  525.             if(!pp) pp = p;
  526.             strcat(newsubj,pp);
  527.             if(!strchr(p,'*') && !strchr(p,'?')) {
  528.                 ffprintf(req,"%s\r\n",p);
  529.             }
  530.             else {  /* expand wildcards */
  531.                 search_handle = 1;
  532.                 num_matches = 1;
  533.                 if(!DosFindFirst(p,&search_handle,0,&f,
  534.                   sizeof(FILEFINDBUF),&num_matches,0L)) {
  535.                       *pp = 0;
  536.                       do {
  537.                           sprintf(tempbuf,"%s%s",p,f.achName);
  538.                           ffprintf(req,"%s\r\n",tempbuf);
  539.                           num_matches = 1;
  540.                       } while(!DosFindNext(search_handle,&f,sizeof(FILEFINDBUF),
  541.                                            &num_matches));
  542.                       DosFindClose(search_handle);
  543.                 }
  544.             }
  545.             p = strtok(0," ");
  546.             if(p) lstrip(p);
  547.             if(p && *p) strcat(newsubj," ");
  548.         }
  549.         if(*newsubj) strcpy(amsg->subj,newsubj);
  550.         close(req);
  551.     }
  552.     else if(amsg->attr & MSGURQ) {  /* update request */
  553.         amsg->attr &= (~MSGURQ);
  554.         if(!control.xbbsos2) {
  555.             sprintf(namebuf,"%s/%04x%04x.REQ",currout,addr->net,addr->node);
  556.         }
  557.         else {
  558.             sprintf(namebuf,"%s/R.%u.%u.%u.%u.%s",outbound,addr->zone,
  559.                     addr->net,addr->node,addr->point,addr->domain);
  560.         }
  561.         req = sopen(namebuf,O_CREAT | O_RDWR | O_BINARY,SH_DENYWR,S_IWRITE);
  562.         if(req == -1) return -1;
  563.         lseek(req,0L,SEEK_END);
  564.         strcpy(namebuf,amsg->subj);
  565.         lstrip(namebuf);
  566.         rstrip(namebuf);
  567.         p = strtok(namebuf," ");
  568.         while(p) {
  569.             while(pp = strchr(p,'\\')) *pp = '/';
  570.                         if(strchr(p,'*') || strchr(p,'?') || !stat(p,&st)) {
  571.                 pp = strrchr(p,'/');
  572.                 if(!pp) pp = strrchr(p,':');
  573.                 if(!pp) pp = p;
  574.                 strcat(newsubj,pp);
  575.             }
  576.                         if(!strchr(p,'*') && !strchr(p,'?')) {
  577.                             ffprintf(req,"%s +%lu\r\n",pp,st.st_mtime);
  578.                         }
  579.                         else {    /* expand wildcards */
  580.                             search_handle = 1;
  581.                             num_matches = 1;
  582.                             if(!DosFindFirst(p,&search_handle,0,&f,
  583.                                 sizeof(FILEFINDBUF),&num_matches,0L)) {
  584.                                     *pp = 0;
  585.                                     do {
  586.                                         sprintf(tempbuf,"%s%s",p,f.achName);
  587.                                         if(!stat(tempbuf,&st)) {
  588.                                             ffprintf(req,"%s +%lu\r\n",f.achName,st.st_mtime);
  589.                                         }
  590.                                         num_matches = 1;
  591.                                     } while(!DosFindNext(search_handle,&f,sizeof(FILEFINDBUF),
  592.                                                                              &num_matches));
  593.                                     DosFindClose(search_handle);
  594.                             }
  595.                         }
  596.                         p = strtok(0," ");
  597.             if(p) lstrip(p);
  598.             if(p && *p) strcat(newsubj," ");
  599.         }
  600.         close(req);
  601.         if(*newsubj) strcat(amsg->subj,newsubj);
  602.     }
  603.  
  604.     return 0;
  605. }
  606.