"Fossies" - the Fresh Open Source Software Archive

Member "citadel/modules/ctdlproto/serv_file.c" (5 Jun 2021, 16870 Bytes) of package /linux/www/citadel.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "serv_file.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.01_vs_902.

    1 /* 
    2  * Server functions which handle file transfers and room directories.
    3  *
    4  * Copyright (c) 1987-2018 by the citadel.org team
    5  *
    6  * This program is open source software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License version 3.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  */
   14 
   15 #include <stdlib.h>
   16 #include <unistd.h>
   17 #include <stdio.h>
   18 #include <netdb.h>
   19 #include <libcitadel.h>
   20 #include <dirent.h>
   21 #include <sys/types.h>
   22 #include <sys/stat.h>
   23 #include "ctdl_module.h"
   24 #include "citserver.h"
   25 #include "support.h"
   26 #include "config.h"
   27 #include "user_ops.h"
   28 
   29 
   30 /*
   31  * Server command to delete a file from a room's directory
   32  */
   33 void cmd_delf(char *filename)
   34 {
   35     char pathname[64];
   36     int a;
   37 
   38     if (CtdlAccessCheck(ac_room_aide))
   39         return;
   40 
   41     if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
   42         cprintf("%d No directory in this room.\n", ERROR + NOT_HERE);
   43         return;
   44     }
   45 
   46     if (IsEmptyStr(filename)) {
   47         cprintf("%d You must specify a file name.\n", ERROR + FILE_NOT_FOUND);
   48         return;
   49     }
   50     for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
   51         if ( (filename[a] == '/') || (filename[a] == '\\') ) {
   52             filename[a] = '_';
   53         }
   54     }
   55     snprintf(pathname, sizeof pathname,
   56          "%s/%s/%s",
   57          ctdl_file_dir,
   58          CC->room.QRdirname, filename
   59     );
   60     a = unlink(pathname);
   61     if (a == 0) {
   62         cprintf("%d File '%s' deleted.\n", CIT_OK, pathname);
   63     }
   64     else {
   65         cprintf("%d File '%s' not found.\n", ERROR + FILE_NOT_FOUND, pathname);
   66     }
   67 }
   68 
   69 
   70 /*
   71  * move a file from one room directory to another
   72  */
   73 void cmd_movf(char *cmdbuf)
   74 {
   75     char filename[PATH_MAX];
   76     char pathname[PATH_MAX];
   77     char newpath[PATH_MAX];
   78     char newroom[ROOMNAMELEN];
   79     char buf[PATH_MAX];
   80     int a;
   81     struct ctdlroom qrbuf;
   82 
   83     extract_token(filename, cmdbuf, 0, '|', sizeof filename);
   84     extract_token(newroom, cmdbuf, 1, '|', sizeof newroom);
   85 
   86     if (CtdlAccessCheck(ac_room_aide)) return;
   87 
   88     if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
   89         cprintf("%d No directory in this room.\n", ERROR + NOT_HERE);
   90         return;
   91     }
   92 
   93     if (IsEmptyStr(filename)) {
   94         cprintf("%d You must specify a file name.\n", ERROR + FILE_NOT_FOUND);
   95         return;
   96     }
   97 
   98     for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
   99         if ( (filename[a] == '/') || (filename[a] == '\\') ) {
  100             filename[a] = '_';
  101         }
  102     }
  103     snprintf(pathname, sizeof pathname, "./files/%s/%s", CC->room.QRdirname, filename);
  104     if (access(pathname, 0) != 0) {
  105         cprintf("%d File '%s' not found.\n", ERROR + FILE_NOT_FOUND, pathname);
  106         return;
  107     }
  108 
  109     if (CtdlGetRoom(&qrbuf, newroom) != 0) {
  110         cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, newroom);
  111         return;
  112     }
  113     if ((qrbuf.QRflags & QR_DIRECTORY) == 0) {
  114         cprintf("%d '%s' is not a directory room.\n", ERROR + NOT_HERE, qrbuf.QRname);
  115         return;
  116     }
  117     snprintf(newpath, sizeof newpath, "./files/%s/%s", qrbuf.QRdirname, filename);
  118     if (link(pathname, newpath) != 0) {
  119         cprintf("%d Couldn't move file: %s\n", ERROR + INTERNAL_ERROR, strerror(errno));
  120         return;
  121     }
  122     unlink(pathname);
  123 
  124     /* this is a crude method of copying the file description */
  125     snprintf(buf, sizeof buf, "cat ./files/%s/filedir |grep \"%s\" >>./files/%s/filedir", CC->room.QRdirname, filename, qrbuf.QRdirname);
  126     system(buf);
  127     cprintf("%d File '%s' has been moved.\n", CIT_OK, filename);
  128 }
  129 
  130 
  131 /*
  132  * This code is common to all commands which open a file for downloading,
  133  * regardless of whether it's a file from the directory, an image, a network
  134  * spool file, a MIME attachment, etc.
  135  * It examines the file and displays the OK result code and some information
  136  * about the file.  NOTE: this stuff is Unix dependent.
  137  */
  138 void OpenCmdResult(char *filename, const char *mime_type)
  139 {
  140     struct stat statbuf;
  141     time_t modtime;
  142     long filesize;
  143 
  144     fstat(fileno(CC->download_fp), &statbuf);
  145     CC->download_fp_total = statbuf.st_size;
  146     filesize = (long) statbuf.st_size;
  147     modtime = (time_t) statbuf.st_mtime;
  148 
  149     cprintf("%d %ld|%ld|%s|%s\n", CIT_OK, filesize, (long)modtime, filename, mime_type);
  150 }
  151 
  152 
  153 /*
  154  * open a file for downloading
  155  */
  156 void cmd_open(char *cmdbuf)
  157 {
  158     char filename[256];
  159     char pathname[PATH_MAX];
  160     int a;
  161 
  162     extract_token(filename, cmdbuf, 0, '|', sizeof filename);
  163 
  164     if (CtdlAccessCheck(ac_logged_in)) return;
  165 
  166     if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
  167         cprintf("%d No directory in this room.\n", ERROR + NOT_HERE);
  168         return;
  169     }
  170 
  171     if (IsEmptyStr(filename)) {
  172         cprintf("%d You must specify a file name.\n", ERROR + FILE_NOT_FOUND);
  173         return;
  174     }
  175     if (strstr(filename, "../") != NULL)
  176     {
  177         cprintf("%d syntax error.\n", ERROR + ILLEGAL_VALUE);
  178         return;
  179     }
  180 
  181     if (CC->download_fp != NULL) {
  182         cprintf("%d You already have a download file open.\n", ERROR + RESOURCE_BUSY);
  183         return;
  184     }
  185 
  186     for (a = 0; !IsEmptyStr(&filename[a]); ++a) {
  187         if ( (filename[a] == '/') || (filename[a] == '\\') ) {
  188             filename[a] = '_';
  189         }
  190     }
  191 
  192     snprintf(pathname, sizeof pathname, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filename);
  193     CC->download_fp = fopen(pathname, "r");
  194 
  195     if (CC->download_fp == NULL) {
  196         cprintf("%d cannot open %s: %s\n", ERROR + INTERNAL_ERROR, pathname, strerror(errno));
  197         return;
  198     }
  199 
  200     OpenCmdResult(filename, "application/octet-stream");
  201 }
  202 
  203 
  204 /*
  205  * open an image file
  206  */
  207 void cmd_oimg(char *cmdbuf)
  208 {
  209     char filename[PATH_MAX];
  210     char pathname[PATH_MAX];
  211     char MimeTestBuf[32];
  212     int rv;
  213 
  214     extract_token(filename, cmdbuf, 0, '|', sizeof filename);
  215 
  216     if (IsEmptyStr(filename)) {
  217         cprintf("%d You must specify a file name.\n", ERROR + FILE_NOT_FOUND);
  218         return;
  219     }
  220 
  221     if (CC->download_fp != NULL) {
  222         cprintf("%d You already have a download file open.\n", ERROR + RESOURCE_BUSY);
  223         return;
  224     }
  225 
  226     CC->download_fp = fopen(pathname, "rb");
  227     if (CC->download_fp == NULL) {
  228         strcat(pathname, ".gif");
  229         CC->download_fp = fopen(pathname, "rb");
  230     }
  231     if (CC->download_fp == NULL) {
  232         cprintf("%d Cannot open %s: %s\n", ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
  233         return;
  234     }
  235     rv = fread(&MimeTestBuf[0], 1, 32, CC->download_fp);
  236     if (rv == -1) {
  237         cprintf("%d Cannot access %s: %s\n", ERROR + FILE_NOT_FOUND, pathname, strerror(errno));
  238         return;
  239     }
  240 
  241     rewind (CC->download_fp);
  242     OpenCmdResult(pathname, GuessMimeType(&MimeTestBuf[0], 32));
  243 }
  244 
  245 
  246 /*
  247  * open a file for uploading
  248  */
  249 void cmd_uopn(char *cmdbuf)
  250 {
  251     int a;
  252 
  253     extract_token(CC->upl_file, cmdbuf, 0, '|', sizeof CC->upl_file);
  254     extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
  255     extract_token(CC->upl_comment, cmdbuf, 2, '|', sizeof CC->upl_comment);
  256 
  257     if (CtdlAccessCheck(ac_logged_in)) return;
  258 
  259     if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
  260         cprintf("%d No directory in this room.\n", ERROR + NOT_HERE);
  261         return;
  262     }
  263 
  264     if (IsEmptyStr(CC->upl_file)) {
  265         cprintf("%d You must specify a file name.\n", ERROR + FILE_NOT_FOUND);
  266         return;
  267     }
  268 
  269     if (CC->upload_fp != NULL) {
  270         cprintf("%d You already have a upload file open.\n", ERROR + RESOURCE_BUSY);
  271         return;
  272     }
  273 
  274     for (a = 0; !IsEmptyStr(&CC->upl_file[a]); ++a) {
  275         if ( (CC->upl_file[a] == '/') || (CC->upl_file[a] == '\\') ) {
  276             CC->upl_file[a] = '_';
  277         }
  278     }
  279     snprintf(CC->upl_path, sizeof CC->upl_path, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, CC->upl_file);
  280     snprintf(CC->upl_filedir, sizeof CC->upl_filedir, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname);
  281 
  282     CC->upload_fp = fopen(CC->upl_path, "r");
  283     if (CC->upload_fp != NULL) {
  284         fclose(CC->upload_fp);
  285         CC->upload_fp = NULL;
  286         cprintf("%d '%s' already exists\n", ERROR + ALREADY_EXISTS, CC->upl_path);
  287         return;
  288     }
  289 
  290     CC->upload_fp = fopen(CC->upl_path, "wb");
  291     if (CC->upload_fp == NULL) {
  292         cprintf("%d Cannot open %s: %s\n", ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
  293         return;
  294     }
  295     cprintf("%d Ok\n", CIT_OK);
  296 }
  297 
  298 
  299 /*
  300  * open an image file for uploading
  301  */
  302 void cmd_uimg(char *cmdbuf)
  303 {
  304     int is_this_for_real;
  305     char basenm[256];
  306     int a;
  307 
  308     if (num_parms(cmdbuf) < 2) {
  309         cprintf("%d Usage error.\n", ERROR + ILLEGAL_VALUE);
  310         return;
  311     }
  312 
  313     is_this_for_real = extract_int(cmdbuf, 0);
  314     extract_token(CC->upl_mimetype, cmdbuf, 1, '|', sizeof CC->upl_mimetype);
  315     extract_token(basenm, cmdbuf, 2, '|', sizeof basenm);
  316     if (CC->upload_fp != NULL) {
  317         cprintf("%d You already have an upload file open.\n", ERROR + RESOURCE_BUSY);
  318         return;
  319     }
  320 
  321     strcpy(CC->upl_path, "");
  322 
  323     for (a = 0; !IsEmptyStr(&basenm[a]); ++a) {
  324         basenm[a] = tolower(basenm[a]);
  325         if ( (basenm[a] == '/') || (basenm[a] == '\\') ) {
  326             basenm[a] = '_';
  327         }
  328     }
  329 
  330     if (CC->user.axlevel >= AxAideU) {
  331         snprintf(CC->upl_path, sizeof CC->upl_path, "%s/%s", ctdl_image_dir, basenm);
  332     }
  333 
  334     if (IsEmptyStr(CC->upl_path)) {
  335         cprintf("%d Higher access required.\n", ERROR + HIGHER_ACCESS_REQUIRED);
  336         return;
  337     }
  338 
  339     if (is_this_for_real == 0) {
  340         cprintf("%d Ok to send image\n", CIT_OK);
  341         return;
  342     }
  343 
  344     CC->upload_fp = fopen(CC->upl_path, "wb");
  345     if (CC->upload_fp == NULL) {
  346         cprintf("%d Cannot open %s: %s\n", ERROR + INTERNAL_ERROR, CC->upl_path, strerror(errno));
  347         return;
  348     }
  349     cprintf("%d Ok\n", CIT_OK);
  350 }
  351 
  352 
  353 /*
  354  * close the download file
  355  */
  356 void cmd_clos(char *cmdbuf)
  357 {
  358     if (CC->download_fp == NULL) {
  359         cprintf("%d You don't have a download file open.\n", ERROR + RESOURCE_NOT_OPEN);
  360         return;
  361     }
  362 
  363     fclose(CC->download_fp);
  364     CC->download_fp = NULL;
  365     cprintf("%d Ok\n", CIT_OK);
  366 }
  367 
  368 
  369 /*
  370  * abort an upload
  371  */
  372 void abort_upl(CitContext *who)
  373 {
  374     if (who->upload_fp != NULL) {
  375         fclose(who->upload_fp);
  376         who->upload_fp = NULL;
  377         unlink(CC->upl_path);
  378     }
  379 }
  380 
  381 
  382 /*
  383  * close the upload file
  384  */
  385 void cmd_ucls(char *cmd)
  386 {
  387     FILE *fp;
  388     char upload_notice[SIZ];
  389 
  390     if (CC->upload_fp == NULL) {
  391         cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
  392         return;
  393     }
  394 
  395     fclose(CC->upload_fp);
  396     CC->upload_fp = NULL;
  397 
  398     if (!strcasecmp(cmd, "1")) {
  399         cprintf("%d File '%s' saved.\n", CIT_OK, CC->upl_path);
  400         fp = fopen(CC->upl_filedir, "a");
  401         if (fp == NULL) {
  402             fp = fopen(CC->upl_filedir, "w");
  403         }
  404         if (fp != NULL) {
  405             fprintf(fp, "%s %s %s\n", CC->upl_file, CC->upl_mimetype, CC->upl_comment);
  406             fclose(fp);
  407         }
  408 
  409         if ((CC->room.QRflags2 & QR2_NOUPLMSG) == 0) {
  410             /* put together an upload notice */
  411             snprintf(upload_notice, sizeof upload_notice,
  412                  "NEW UPLOAD: '%s'\n %s\n%s\n",
  413                  CC->upl_file, 
  414                  CC->upl_comment, 
  415                  CC->upl_mimetype
  416             );
  417             quickie_message(CC->curr_user, NULL, NULL, CC->room.QRname, upload_notice, 0, NULL);
  418         }
  419     } else {
  420         abort_upl(CC);
  421         cprintf("%d File '%s' aborted.\n", CIT_OK, CC->upl_path);
  422     }
  423 }
  424 
  425 
  426 /*
  427  * read from the download file
  428  */
  429 void cmd_read(char *cmdbuf)
  430 {
  431     long start_pos;
  432     size_t bytes;
  433     char buf[SIZ];
  434     int rc;
  435 
  436     /* The client will transmit its requested offset and byte count */
  437     start_pos = extract_long(cmdbuf, 0);
  438     bytes = extract_int(cmdbuf, 1);
  439     if ((start_pos < 0) || (bytes <= 0)) {
  440         cprintf("%d you have to specify a value > 0.\n", ERROR + ILLEGAL_VALUE);
  441         return;
  442     }
  443 
  444     if (CC->download_fp == NULL) {
  445         cprintf("%d You don't have a download file open.\n", ERROR + RESOURCE_NOT_OPEN);
  446         return;
  447     }
  448 
  449     /* If necessary, reduce the byte count to the size of our buffer */
  450     if (bytes > sizeof(buf)) {
  451         bytes = sizeof(buf);
  452     }
  453 
  454     rc = fseek(CC->download_fp, start_pos, 0);
  455     if (rc < 0) {
  456         cprintf("%d your file is smaller than %ld.\n", ERROR + ILLEGAL_VALUE, start_pos);
  457         syslog(LOG_ERR, "serv_file: your file %s is smaller than %ld [%s]", 
  458             CC->upl_path, 
  459             start_pos,
  460             strerror(errno)
  461         );
  462 
  463         return;
  464     }
  465     bytes = fread(buf, 1, bytes, CC->download_fp);
  466     if (bytes > 0) {
  467         /* Tell the client the actual byte count and transmit it */
  468         cprintf("%d %d\n", BINARY_FOLLOWS, (int)bytes);
  469         client_write(buf, bytes);
  470     }
  471     else {
  472         cprintf("%d %s\n", ERROR, strerror(errno));
  473     }
  474 }
  475 
  476 
  477 /*
  478  * write to the upload file
  479  */
  480 void cmd_writ(char *cmdbuf)
  481 {
  482     struct CitContext *CCC = CC;
  483     int bytes;
  484     char *buf;
  485     int rv;
  486 
  487     unbuffer_output();
  488 
  489     bytes = extract_int(cmdbuf, 0);
  490 
  491     if (CCC->upload_fp == NULL) {
  492         cprintf("%d You don't have an upload file open.\n", ERROR + RESOURCE_NOT_OPEN);
  493         return;
  494     }
  495     if (bytes <= 0) {
  496         cprintf("%d you have to specify a value > 0.\n", ERROR + ILLEGAL_VALUE);
  497         return;
  498     }
  499 
  500     if (bytes > 100000) {
  501         bytes = 100000;
  502     }
  503 
  504     cprintf("%d %d\n", SEND_BINARY, bytes);
  505     buf = malloc(bytes + 1);
  506     client_read(buf, bytes);
  507     rv = fwrite(buf, bytes, 1, CCC->upload_fp);
  508     if (rv == -1) {
  509         syslog(LOG_ERR, "serv_file: %s", strerror(errno));
  510     }
  511     free(buf);
  512 }
  513 
  514 
  515 void files_logout_hook(void)
  516 {
  517         CitContext *CCC = MyContext();
  518 
  519     /*
  520      * If there is a download in progress, abort it.
  521      */
  522     if (CCC->download_fp != NULL) {
  523         fclose(CCC->download_fp);
  524         CCC->download_fp = NULL;
  525     }
  526 
  527     /*
  528      * If there is an upload in progress, abort it.
  529      */
  530     if (CCC->upload_fp != NULL) {
  531         abort_upl(CCC);
  532     }
  533 
  534 }
  535 
  536 
  537 /* 
  538  * help_subst()  -  support routine for help file viewer
  539  */
  540 void help_subst(char *strbuf, char *source, char *dest)
  541 {
  542     char workbuf[SIZ];
  543     int p;
  544 
  545     while (p = pattern2(strbuf, source), (p >= 0)) {
  546         strcpy(workbuf, &strbuf[p + strlen(source)]);
  547         strcpy(&strbuf[p], dest);
  548         strcat(strbuf, workbuf);
  549     }
  550 }
  551 
  552 
  553 void do_help_subst(char *buffer)
  554 {
  555     char buf2[16];
  556 
  557     help_subst(buffer, "^nodename", CtdlGetConfigStr("c_nodename"));
  558     help_subst(buffer, "^humannode", CtdlGetConfigStr("c_humannode"));
  559     help_subst(buffer, "^fqdn", CtdlGetConfigStr("c_fqdn"));
  560     help_subst(buffer, "^username", CC->user.fullname);
  561     snprintf(buf2, sizeof buf2, "%ld", CC->user.usernum);
  562     help_subst(buffer, "^usernum", buf2);
  563     help_subst(buffer, "^sysadm", CtdlGetConfigStr("c_sysadm"));
  564     help_subst(buffer, "^variantname", CITADEL);
  565     help_subst(buffer, "^maxsessions", CtdlGetConfigStr("c_maxsessions"));      // yes it's numeric but str is ok here
  566     help_subst(buffer, "^bbsdir", ctdl_message_dir);
  567 }
  568 
  569 
  570 typedef const char *ccharp;
  571 /*
  572  * display system messages or help
  573  */
  574 void cmd_mesg(char *mname)
  575 {
  576     FILE *mfp;
  577     char targ[256];
  578     char buf[256];
  579     char buf2[256];
  580     DIR *dp;
  581     struct dirent *d;
  582 
  583     extract_token(buf, mname, 0, '|', sizeof buf);
  584 
  585     snprintf(buf2, sizeof buf2, "%s.%d.%d", buf, CC->cs_clientdev, CC->cs_clienttyp);
  586 
  587     /* If the client requested "?" then produce a listing */
  588     if (!strcmp(buf, "?")) {
  589         cprintf("%d %s\n", LISTING_FOLLOWS, buf);
  590         dp = opendir(ctdl_message_dir);
  591         if (dp != NULL) {
  592             while (d = readdir(dp), d != NULL) {
  593                 if (d->d_name[0] != '.') {
  594                     cprintf(" %s\n", d->d_name);
  595                 }
  596             }
  597             closedir(dp);
  598         }
  599         cprintf("000\n");
  600         return;
  601     }
  602 
  603     /* Otherwise, look for the requested file by name. */
  604     snprintf(targ, sizeof targ, "%s/%s", ctdl_message_dir, buf);
  605     mfp = fopen(targ, "r");
  606     if (mfp==NULL) {
  607         cprintf("%d Cannot open '%s': %s\n",
  608             ERROR + FILE_NOT_FOUND, targ, strerror(errno));
  609         return;
  610     }
  611     cprintf("%d %s\n", LISTING_FOLLOWS, buf);
  612 
  613     while (fgets(buf, (sizeof buf - 1), mfp) != NULL) {
  614         buf[strlen(buf)-1] = 0;
  615         do_help_subst(buf);
  616         cprintf("%s\n",buf);
  617     }
  618 
  619     fclose(mfp);
  620     cprintf("000\n");
  621 }
  622 
  623 
  624 /*
  625  * enter system messages or help
  626  */
  627 void cmd_emsg(char *mname)
  628 {
  629     FILE *mfp;
  630     char targ[256];
  631     char buf[256];
  632     int a;
  633 
  634     unbuffer_output();
  635 
  636     if (CtdlAccessCheck(ac_aide)) return;
  637 
  638     extract_token(buf, mname, 0, '|', sizeof buf);
  639     for (a=0; !IsEmptyStr(&buf[a]); ++a) {      /* security measure */
  640         if (buf[a] == '/') buf[a] = '.';
  641     }
  642 
  643     if (IsEmptyStr(targ)) {
  644         snprintf(targ, sizeof targ, "%s/%s", ctdl_message_dir, buf);
  645     }
  646 
  647     mfp = fopen(targ, "w");
  648     if (mfp==NULL) {
  649         cprintf("%d Cannot open '%s': %s\n",
  650             ERROR + INTERNAL_ERROR, targ, strerror(errno));
  651         return;
  652     }
  653     cprintf("%d %s\n", SEND_LISTING, targ);
  654 
  655     while (client_getln(buf, sizeof buf) >=0 && strcmp(buf, "000")) {
  656         fprintf(mfp, "%s\n", buf);
  657     }
  658 
  659     fclose(mfp);
  660 }
  661 
  662 /*****************************************************************************/
  663 /*                      MODULE INITIALIZATION STUFF                          */
  664 /*****************************************************************************/
  665 
  666 CTDL_MODULE_INIT(file_ops)
  667 {
  668     if (!threading) {
  669                 CtdlRegisterSessionHook(files_logout_hook, EVT_LOGOUT, PRIO_LOGOUT + 8);
  670         CtdlRegisterProtoHook(cmd_delf, "DELF", "Delete a file");
  671         CtdlRegisterProtoHook(cmd_movf, "MOVF", "Move a file");
  672         CtdlRegisterProtoHook(cmd_open, "OPEN", "Open a download file transfer");
  673         CtdlRegisterProtoHook(cmd_clos, "CLOS", "Close a download file transfer");
  674         CtdlRegisterProtoHook(cmd_uopn, "UOPN", "Open an upload file transfer");
  675         CtdlRegisterProtoHook(cmd_ucls, "UCLS", "Close an upload file transfer");
  676         CtdlRegisterProtoHook(cmd_read, "READ", "File transfer read operation");
  677         CtdlRegisterProtoHook(cmd_writ, "WRIT", "File transfer write operation");
  678         CtdlRegisterProtoHook(cmd_oimg, "OIMG", "Open an image file for download");
  679         CtdlRegisterProtoHook(cmd_uimg, "UIMG", "Upload an image file");
  680         CtdlRegisterProtoHook(cmd_mesg, "MESG", "fetch system banners");
  681         CtdlRegisterProtoHook(cmd_emsg, "EMSG", "submit system banners");
  682     }
  683         /* return our Subversion id for the Log */
  684     return "file_ops";
  685 }