]> code.citadel.org Git - citadel.git/blob - citadel/citadel_ipc.c
* Added MSG4 support to client-side IPC
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #include "sysdep.h"
4 #if TIME_WITH_SYS_TIME
5 # include <sys/time.h>
6 # include <time.h>
7 #else
8 # if HAVE_SYS_TIME_H
9 #  include <sys/time.h>
10 # else
11 #  include <time.h>
12 # endif
13 #endif
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <string.h>
17 #include <malloc.h>
18 #include <stdlib.h>
19 #include <ctype.h>
20 #ifdef THREADED_CLIENT
21 #include <pthread.h>
22 #endif
23 #include "citadel.h"
24 #include "citadel_ipc.h"
25 #include "citadel_decls.h"
26 #include "client_crypto.h"
27 #include "tools.h"
28
29 #ifdef THREADED_CLIENT
30 pthread_mutex_t rwlock;
31 #endif
32 char express_msgs = 0;
33
34 static volatile int download_in_progress = 0;   /* download file open */
35 static volatile int upload_in_progress = 0;     /* upload file open */
36 /* static volatile int serv_sock; */    /* Socket on which we talk to server */
37
38
39 /*
40  * Does nothing.  The server should always return 200.
41  */
42 int CtdlIPCNoop(void)
43 {
44         char aaa[128];
45
46         return CtdlIPCGenericCommand("NOOP", NULL, 0, NULL, NULL, aaa);
47 }
48
49
50 /*
51  * Does nothing interesting.  The server should always return 200
52  * along with your string.
53  */
54 int CtdlIPCEcho(const char *arg, char *cret)
55 {
56         register int ret;
57         char *aaa;
58         
59         if (!arg) return -2;
60         if (!cret) return -2;
61
62         aaa = (char *)malloc((size_t)(strlen(arg) + 6));
63         if (!aaa) return -1;
64
65         sprintf(aaa, "ECHO %s", arg);
66         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
67         free(aaa);
68         return ret;
69 }
70
71
72 /*
73  * Asks the server to close the connecction.
74  * Should always return 200.
75  */
76 int CtdlIPCQuit(void)
77 {
78         register int ret;
79         char aaa[128];
80
81         netio_lock();
82         serv_puts("QUIT");
83         serv_gets(aaa);
84         ret = atoi(aaa);
85         netio_unlock();
86         return ret;
87 }
88
89
90 /*
91  * Asks the server to logout.  Should always return 200, even if no user
92  * was logged in.  The user will not be logged in after this!
93  */
94 int CtdlIPCLogout(void)
95 {
96         register int ret;
97         char aaa[128];
98
99         netio_lock();
100         serv_puts("LOUT");
101         serv_gets(aaa);
102         ret = atoi(aaa);
103         netio_unlock();
104         return ret;
105 }
106
107
108 /*
109  * First stage of authentication - pass the username.  Returns 300 if the
110  * username is able to log in, with the username correctly spelled in cret.
111  * Returns various 500 error codes if the user doesn't exist, etc.
112  */
113 int CtdlIPCTryLogin(const char *username, char *cret)
114 {
115         register int ret;
116         char *aaa;
117
118         if (!username) return -2;
119         if (!cret) return -2;
120
121         aaa = (char *)malloc((size_t)(strlen(username) + 6));
122         if (!aaa) return -1;
123
124         sprintf(aaa, "USER %s", username);
125         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
126         free(aaa);
127         return ret;
128 }
129
130
131 /*
132  * Second stage of authentication - provide password.  The server returns
133  * 200 and several arguments in cret relating to the user's account.
134  */
135 int CtdlIPCTryPassword(const char *passwd, char *cret)
136 {
137         register int ret;
138         char *aaa;
139
140         if (!passwd) return -2;
141         if (!cret) return -2;
142
143         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
144         if (!aaa) return -1;
145
146         sprintf(aaa, "PASS %s", passwd);
147         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
148         free(aaa);
149         return ret;
150 }
151
152
153 /*
154  * Create a new user.  This returns 200 plus the same arguments as TryPassword
155  * if selfservice is nonzero, unless there was a problem creating the account.
156  * If selfservice is zero, creates a new user but does not log out the existing
157  * user - intended for use by system administrators to create accounts on
158  * behalf of other users.
159  */
160 int CtdlIPCCreateUser(const char *username, int selfservice, char *cret)
161 {
162         register int ret;
163         char *aaa;
164
165         if (!username) return -2;
166         if (!cret) return -2;
167
168         aaa = (char *)malloc((size_t)(strlen(username) + 6));
169         if (!aaa) return -1;
170
171         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
172         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
173         free(aaa);
174         return ret;
175 }
176
177
178 /*
179  * Changes the user's password.  Returns 200 if changed, errors otherwise.
180  */
181 int CtdlIPCChangePassword(const char *passwd, char *cret)
182 {
183         register int ret;
184         char *aaa;
185
186         if (!passwd) return -2;
187         if (!cret) return -2;
188
189         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
190         if (!aaa) return -1;
191
192         sprintf(aaa, "SETP %s", passwd);
193         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
194         free(aaa);
195         return ret;
196 }
197
198
199 /* LKRN */
200 /* Caller must free the march list */
201 /* which is 0 = LRMS, 1 = LKRN, 2 = LKRO, 3 = LKRA, 4 = LZRM */
202 /* floor is -1 for all, or floornum */
203 int CtdlIPCKnownRooms(int which, int floor, struct march **listing, char *cret)
204 {
205         register int ret;
206         struct march *march = NULL;
207         static char *proto[] = {"LRMS", "LKRN", "LKRO", "LKRA", "LZRM" };
208         char aaa[SIZ];
209         char *bbb = NULL;
210         size_t bbbsize;
211
212         if (!listing) return -2;
213         if (*listing) return -2;        /* Free the listing first */
214         if (!cret) return -2;
215         if (which < 0 || which > 4) return -2;
216         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
217
218         sprintf(aaa, "%s %d", proto[which], floor);
219         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
220         if (ret / 100 == 1) {
221                 struct march *mptr;
222
223                 while (bbb && strlen(bbb)) {
224                         int a;
225
226                         extract_token(aaa, bbb, 0, '\n');
227                         a = strlen(aaa);
228                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
229                         mptr = (struct march *) malloc(sizeof (struct march));
230                         if (mptr) {
231                                 mptr->next = NULL;
232                                 extract(mptr->march_name, aaa, 0);
233                                 mptr->march_floor = (char) extract_int(aaa, 2);
234                                 mptr->march_order = (char) extract_int(aaa, 3);
235                                 if (march == NULL)
236                                         march = mptr;
237                                 else {
238                                         struct march *mptr2;
239
240                                         mptr2 = march;
241                                         while (mptr2->next != NULL)
242                                                 mptr2 = mptr2->next;
243                                         mptr2->next = mptr;
244                                 }
245                         }
246                 }
247         }
248         *listing = march;
249         return ret;
250 }
251
252
253 /* GETU */
254 /* Caller must free the struct usersupp; caller may pass an existing one */
255 int CtdlIPCGetConfig(struct usersupp **uret, char *cret)
256 {
257         register int ret;
258
259         if (!cret) return -2;
260         if (!uret) return -2;
261         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
262         if (!*uret) return -1;
263
264         ret = CtdlIPCGenericCommand("GETU", NULL, 0, NULL, NULL, cret);
265         if (ret / 100 == 2) {
266                 uret[0]->USscreenwidth = extract_int(cret, 0);
267                 uret[0]->USscreenheight = extract_int(cret, 1);
268                 uret[0]->flags = extract_int(cret, 2);
269         }
270         return ret;
271 }
272
273
274 /* SETU */
275 int CtdlIPCSetConfig(struct usersupp *uret, char *cret)
276 {
277         char aaa[48];
278
279         if (!uret) return -2;
280         if (!cret) return -2;
281
282         sprintf(aaa, "SETU %d|%d|%d",
283                         uret->USscreenwidth, uret->USscreenheight,
284                         uret->flags);
285         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
286 }
287
288
289 /* GOTO */
290 int CtdlIPCGotoRoom(const char *room, const char *passwd,
291                 struct ctdlipcroom **rret, char *cret)
292 {
293         register int ret;
294         char *aaa;
295
296         if (!cret) return -2;
297         if (!rret) return -2;
298         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
299         if (!*rret) return -1;
300
301         if (passwd) {
302                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
303                 if (!aaa) {
304                         free(*rret);
305                         return -1;
306                 }
307                 sprintf(aaa, "GOTO %s|%s", room, passwd);
308         } else {
309                 aaa = (char *)malloc(strlen(room) + 6);
310                 if (!aaa) {
311                         free(*rret);
312                         return -1;
313                 }
314                 sprintf(aaa, "GOTO %s", room);
315         }
316         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
317         if (ret / 100 == 2) {
318                 extract(rret[0]->RRname, cret, 0);
319                 rret[0]->RRunread = extract_long(cret, 1);
320                 rret[0]->RRtotal = extract_long(cret, 2);
321                 rret[0]->RRinfoupdated = extract_int(cret, 3);
322                 rret[0]->RRflags = extract_int(cret, 4);
323                 rret[0]->RRhighest = extract_long(cret, 5);
324                 rret[0]->RRlastread = extract_long(cret, 6);
325                 rret[0]->RRismailbox = extract_int(cret, 7);
326                 rret[0]->RRaide = extract_int(cret, 8);
327                 rret[0]->RRnewmail = extract_long(cret, 9);
328                 rret[0]->RRfloor = extract_int(cret, 10);
329         } else {
330                 free(*rret);
331         }
332         return ret;
333 }
334
335
336 /* MSGS */
337 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
338 /* whicharg is number of messages, applies to last, first, gt, lt */
339 int CtdlIPCGetMessages(int which, int whicharg, const char *template,
340                 long **mret, char *cret)
341 {
342         register int ret;
343         register long count = 0;
344         static char *proto[] =
345                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
346         char aaa[33];
347         char *bbb;
348         size_t bbbsize;
349
350         if (!cret) return -2;
351         if (!mret) return -2;
352         if (*mret) return -2;
353         if (which < 0 || which > 6) return -2;
354
355         if (which <= 2)
356                 sprintf(aaa, "MSGS %s||%d", proto[which],
357                                 (template) ? 1 : 0);
358         else
359                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
360                                 (template) ? 1 : 0);
361         if (template) count = strlen(template);
362         ret = CtdlIPCGenericCommand(aaa, template, count, &bbb, &bbbsize, cret);
363         count = 0;
364         while (strlen(bbb)) {
365                 int a;
366
367                 extract_token(aaa, bbb, 0, '\n');
368                 a = strlen(aaa);
369                 memmove(aaa, bbb + a + 1, strlen(bbb) - a - 1);
370                 *mret = (long *)realloc(mret,
371                                         (size_t)((count + 1) * sizeof (long)));
372                 if (*mret)
373                         *mret[count++] = atol(aaa);
374                 *mret[count] = 0L;
375         }
376         return ret;
377 }
378
379
380 /* MSG0, MSG2 */
381 int CtdlIPCGetSingleMessage(long msgnum, int headers, int as_mime,
382                 struct ctdlipcmessage **mret, char *cret)
383 {
384         register int ret;
385         char aaa[SIZ];
386         char *bbb = NULL;
387         size_t bbbsize;
388         int multipart_hunting = 0;
389         char multipart_prefix[SIZ];
390
391         if (!cret) return -1;
392         if (!mret) return -1;
393         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
394         if (!*mret) return -1;
395         if (!msgnum) return -1;
396
397         strcpy(mret[0]->content_type, "");
398         sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
399         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
400         if (ret / 100 == 1) {
401                 if (as_mime != 2) {
402                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
403                         while (strlen(bbb) > 4 && bbb[4] == '=') {
404                                 extract_token(aaa, bbb, 0, '\n');
405                                 remove_token(bbb, 0, '\n');
406
407                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
408                                         mret[0]->nhdr = 1;
409                                 else if (!strncasecmp(aaa, "from=", 5))
410                                         strcpy(mret[0]->author, &aaa[5]);
411                                 else if (!strncasecmp(aaa, "type=", 5))
412                                         mret[0]->type = atoi(&aaa[5]);
413                                 else if (!strncasecmp(aaa, "msgn=", 5))
414                                         strcpy(mret[0]->msgid, &aaa[5]);
415                                 else if (!strncasecmp(aaa, "subj=", 5))
416                                         strcpy(mret[0]->subject, &aaa[5]);
417                                 else if (!strncasecmp(aaa, "rfca=", 5))
418                                         strcpy(mret[0]->email, &aaa[5]);
419                                 else if (!strncasecmp(aaa, "hnod=", 5))
420                                         strcpy(mret[0]->hnod, &aaa[5]);
421                                 else if (!strncasecmp(aaa, "room=", 5))
422                                         strcpy(mret[0]->room, &aaa[5]);
423                                 else if (!strncasecmp(aaa, "node=", 5))
424                                         strcpy(mret[0]->node, &aaa[5]);
425                                 else if (!strncasecmp(aaa, "rcpt=", 5))
426                                         strcpy(mret[0]->recipient, &aaa[5]);
427                                 else if (!strncasecmp(aaa, "time=", 5))
428                                         mret[0]->time = atol(&aaa[5]);
429
430                                 /* Multipart/alternative prefix & suffix strings help
431                                  * us to determine which part we want to download.
432                                  */
433                                 else if (!strncasecmp(aaa, "pref=", 5)) {
434                                         extract(multipart_prefix, &aaa[5], 1);
435                                         if (!strcasecmp(multipart_prefix,
436                                            "multipart/alternative")) {
437                                                 ++multipart_hunting;
438                                         }
439                                 }
440                                 else if (!strncasecmp(aaa, "suff=", 5)) {
441                                         extract(multipart_prefix, &aaa[5], 1);
442                                         if (!strcasecmp(multipart_prefix,
443                                            "multipart/alternative")) {
444                                                 ++multipart_hunting;
445                                         }
446                                 }
447
448                                 else if (!strncasecmp(aaa, "part=", 5)) {
449                                         struct parts *ptr, *chain;
450         
451                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
452                                         if (ptr) {
453
454                                                 /* Fill the buffers for the caller */
455                                                 extract(ptr->name, &aaa[5], 0);
456                                                 extract(ptr->filename, &aaa[5], 1);
457                                                 extract(ptr->number, &aaa[5], 2);
458                                                 extract(ptr->disposition, &aaa[5], 3);
459                                                 extract(ptr->mimetype, &aaa[5], 4);
460                                                 ptr->length = extract_long(&aaa[5], 5);
461                                                 if (!mret[0]->attachments)
462                                                         mret[0]->attachments = ptr;
463                                                 else {
464                                                         chain = mret[0]->attachments;
465                                                         while (chain->next)
466                                                                 chain = chain->next;
467                                                         chain->next = ptr;
468                                                 }
469
470                                                 /* Now handle multipart/alternative */
471                                                 if (multipart_hunting > 0) {
472                                                         if ( (!strcasecmp(ptr->mimetype,
473                                                              "text/plain"))
474                                                            || (!strcasecmp(ptr->mimetype,
475                                                               "text/html")) ) {
476                                                                 strcpy(mret[0]->mime_chosen,
477                                                                         ptr->number);
478                                                         }
479                                                 }
480
481                                         }
482                                 }
483                         }
484                         /* Eliminate "text\n" */
485                         remove_token(bbb, 0, '\n');
486
487                         /* If doing a MIME thing, pull out the extra headers */
488                         if (as_mime == 4) {
489                                 do {
490                                         if (!strncasecmp(bbb, "Content-type: ", 14)) {
491                                                 extract_token(mret[0]->content_type, bbb, 0, '\n');
492                                                 strcpy(mret[0]->content_type,
493                                                         &mret[0]->content_type[14]);
494                                                 striplt(mret[0]->content_type);
495                                         }
496                                         remove_token(bbb, 0, '\n');
497                                 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
498                         }
499
500
501                 }
502                 if (strlen(bbb)) {
503                         /* Strip trailing whitespace */
504                         bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
505                 } else {
506                         bbb = (char *)realloc(bbb, 1);
507                         *bbb = '\0';
508                 }
509                 mret[0]->text = bbb;
510         }
511         return ret;
512 }
513
514
515 /* WHOK */
516 int CtdlIPCWhoKnowsRoom(char **listing, char *cret)
517 {
518         register int ret;
519         size_t bytes;
520
521         if (!cret) return -2;
522         if (!listing) return -2;
523         if (*listing) return -2;
524
525         ret = CtdlIPCGenericCommand("WHOK", NULL, 0, listing, &bytes, cret);
526         return ret;
527 }
528
529
530 /* INFO */
531 int CtdlIPCServerInfo(struct CtdlServInfo *ServInfo, char *cret)
532 {
533         register int ret;
534         size_t bytes;
535         char *listing = NULL;
536         char buf[SIZ];
537
538         if (!cret) return -2;
539         if (!ServInfo) return -2;
540
541         ret = CtdlIPCGenericCommand("INFO", NULL, 0, &listing, &bytes, cret);
542         if (ret / 100 == 1) {
543                 int line = 0;
544
545                 while (*listing && strlen(listing)) {
546                         extract_token(buf, listing, 0, '\n');
547                         remove_token(listing, 0, '\n');
548                         switch (line++) {
549                         case 0:         ServInfo->serv_pid = atoi(buf);
550                                         break;
551                         case 1:         strcpy(ServInfo->serv_nodename,buf);
552                                         break;
553                         case 2:         strcpy(ServInfo->serv_humannode,buf);
554                                         break;
555                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
556                                         break;
557                         case 4:         strcpy(ServInfo->serv_software,buf);
558                                         break;
559                         case 5:         ServInfo->serv_rev_level = atoi(buf);
560                                         break;
561                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
562                                         break;
563                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
564                                         break;
565                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
566                                         break;
567                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
568                                         break;
569                         case 11:        ServInfo->serv_paging_level = atoi(buf);
570                                         break;
571                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
572                                         break;
573                         }
574                 }
575
576         }
577         return ret;
578 }
579
580
581 /* RDIR */
582 int CtdlIPCReadDirectory(char **listing, char *cret)
583 {
584         register int ret;
585         size_t bytes;
586
587         if (!cret) return -2;
588         if (!listing) return -2;
589         if (*listing) return -2;
590
591         ret = CtdlIPCGenericCommand("RDIR", NULL, 0, listing, &bytes, cret);
592         return ret;
593 }
594
595
596 /*
597  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
598  */
599 int CtdlIPCSetLastRead(long msgnum, char *cret)
600 {
601         register int ret;
602         char aaa[16];
603
604         if (!cret) return -2;
605
606         if (msgnum)
607                 sprintf(aaa, "SLRP %ld", msgnum);
608         else
609                 sprintf(aaa, "SLRP HIGHEST");
610         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
611         return ret;
612 }
613
614
615 /* INVT */
616 int CtdlIPCInviteUserToRoom(const char *username, char *cret)
617 {
618         register int ret;
619         char *aaa;
620
621         if (!cret) return -2;
622         if (!username) return -2;
623
624         aaa = (char *)malloc(strlen(username) + 6);
625         if (!aaa) return -1;
626
627         sprintf(aaa, "INVT %s", username);
628         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
629         free(aaa);
630         return ret;
631 }
632
633
634 /* KICK */
635 int CtdlIPCKickoutUserFromRoom(const char *username, char *cret)
636 {
637         register int ret;
638         char *aaa;
639
640         if (!cret) return -1;
641         if (!username) return -1;
642
643         aaa = (char *)malloc(strlen(username) + 6);
644
645         sprintf(aaa, "KICK %s", username);
646         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
647         free(aaa);
648         return ret;
649 }
650
651
652 /* GETR */
653 int CtdlIPCGetRoomAttributes(struct quickroom **qret, char *cret)
654 {
655         register int ret;
656
657         if (!cret) return -2;
658         if (!qret) return -2;
659         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
660         if (!*qret) return -1;
661
662         ret = CtdlIPCGenericCommand("GETR", NULL, 0, NULL, NULL, cret);
663         if (ret / 100 == 2) {
664                 extract(qret[0]->QRname, cret, 0);
665                 extract(qret[0]->QRpasswd, cret, 1);
666                 extract(qret[0]->QRdirname, cret, 2);
667                 qret[0]->QRflags = extract_int(cret, 3);
668                 qret[0]->QRfloor = extract_int(cret, 4);
669                 qret[0]->QRorder = extract_int(cret, 5);
670         }
671         return ret;
672 }
673
674
675 /* SETR */
676 /* set forget to kick all users out of room */
677 int CtdlIPCSetRoomAttributes(int forget, struct quickroom *qret, char *cret)
678 {
679         register int ret;
680         char *aaa;
681
682         if (!cret) return -2;
683         if (!qret) return -2;
684
685         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
686                         strlen(qret->QRdirname) + 52);
687         if (!aaa) return -1;
688
689         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
690                         qret->QRname, qret->QRpasswd, qret->QRdirname,
691                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
692         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
693         free(aaa);
694         return ret;
695 }
696
697
698 /* GETA */
699 int CtdlIPCGetRoomAide(char *cret)
700 {
701         if (!cret) return -1;
702
703         return CtdlIPCGenericCommand("GETA", NULL, 0, NULL, NULL, cret);
704 }
705
706
707 /* SETA */
708 int CtdlIPCSetRoomAide(const char *username, char *cret)
709 {
710         register int ret;
711         char *aaa;
712
713         if (!cret) return -2;
714         if (!username) return -2;
715
716         aaa = (char *)malloc(strlen(username) + 6);
717         if (!aaa) return -1;
718
719         sprintf(aaa, "SETA %s", username);
720         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
721         free(aaa);
722         return ret;
723 }
724
725
726 /* ENT0 */
727 int CtdlIPCPostMessage(int flag, const struct ctdlipcmessage *mr, char *cret)
728 {
729         register int ret;
730         char *aaa;
731
732         if (!cret) return -2;
733         if (!mr) return -2;
734
735         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
736         if (!aaa) return -1;
737
738         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s", flag, mr->recipient, mr->anonymous,
739                         mr->type, mr->author);
740         ret = CtdlIPCGenericCommand(aaa, mr->text, strlen(mr->text), NULL,
741                         NULL, cret);
742         free(aaa);
743         return ret;
744 }
745
746
747 /* RINF */
748 int CtdlIPCRoomInfo(char **iret, char *cret)
749 {
750         size_t bytes;
751
752         if (!cret) return -2;
753         if (!iret) return -2;
754         if (*iret) return -2;
755
756         return CtdlIPCGenericCommand("RINF", NULL, 0, iret, &bytes, cret);
757 }
758
759
760 /* DELE */
761 int CtdlIPCDeleteMessage(long msgnum, char *cret)
762 {
763         char aaa[16];
764
765         if (!cret) return -2;
766         if (!msgnum) return -2;
767
768         sprintf(aaa, "DELE %ld", msgnum);
769         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
770 }
771
772
773 /* MOVE */
774 int CtdlIPCMoveMessage(int copy, long msgnum, const char *destroom, char *cret)
775 {
776         register int ret;
777         char *aaa;
778
779         if (!cret) return -2;
780         if (!destroom) return -2;
781         if (!msgnum) return -2;
782
783         aaa = (char *)malloc(strlen(destroom) + 28);
784         if (!aaa) return -1;
785
786         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
787         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
788         free(aaa);
789         return ret;
790 }
791
792
793 /* KILL */
794 int CtdlIPCDeleteRoom(int for_real, char *cret)
795 {
796         char aaa[16];
797
798         if (!cret) return -2;
799
800         sprintf(aaa, "KILL %d", for_real);
801         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
802 }
803
804
805 /* CRE8 */
806 int CtdlIPCCreateRoom(int for_real, const char *roomname, int type,
807                 const char *password, int floor, char *cret)
808 {
809         register int ret;
810         char *aaa;
811
812         if (!cret) return -2;
813         if (!roomname) return -2;
814
815         if (password) {
816                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
817                 if (!aaa) return -1;
818                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
819                                 password, floor);
820         } else {
821                 aaa = (char *)malloc(strlen(roomname) + 40);
822                 if (!aaa) return -1;
823                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
824                                 floor);
825         }
826         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
827         free(aaa);
828         return ret;
829 }
830
831
832 /* FORG */
833 int CtdlIPCForgetRoom(char *cret)
834 {
835         if (!cret) return -2;
836
837         return CtdlIPCGenericCommand("FORG", NULL, 0, NULL, NULL, cret);
838 }
839
840
841 /* MESG */
842 int CtdlIPCSystemMessage(const char *message, char **mret, char *cret)
843 {
844         register int ret;
845         char *aaa;
846         size_t bytes;
847
848         if (!cret) return -2;
849         if (!mret) return -2;
850         if (*mret) return -2;
851         if (!message) return -2;
852
853         aaa = (char *)malloc(strlen(message) + 6);
854         if (!aaa) return -1;
855
856         sprintf(aaa, "MESG %s", message);
857         ret = CtdlIPCGenericCommand(aaa, NULL, 0, mret, &bytes, cret);
858         free(aaa);
859         return ret;
860 }
861
862
863 /* GNUR */
864 int CtdlIPCNextUnvalidatedUser(char *cret)
865 {
866         if (!cret) return -2;
867
868         return CtdlIPCGenericCommand("GNUR", NULL, 0, NULL, NULL, cret);
869 }
870
871
872 /* GREG */
873 int CtdlIPCGetUserRegistration(const char *username, char **rret, char *cret)
874 {
875         register int ret;
876         char *aaa;
877         size_t bytes;
878
879         if (!cret) return -2;
880         if (!rret) return -2;
881         if (*rret) return -2;
882
883         if (username)
884                 aaa = (char *)malloc(strlen(username) + 6);
885         else
886                 aaa = (char *)malloc(12);
887         if (!aaa) return -1;
888
889         if (username)
890                 sprintf(aaa, "GREG %s", username);
891         else
892                 sprintf(aaa, "GREG _SELF_");
893         ret = CtdlIPCGenericCommand(aaa, NULL, 0, rret, &bytes, cret);
894         free(aaa);
895         return ret;
896 }
897
898
899 /* VALI */
900 int CtdlIPCValidateUser(const char *username, int axlevel, char *cret)
901 {
902         register int ret;
903         char *aaa;
904
905         if (!cret) return -2;
906         if (!username) return -2;
907         if (axlevel < 0 || axlevel > 7) return -2;
908
909         aaa = (char *)malloc(strlen(username) + 17);
910         if (!aaa) return -1;
911
912         sprintf(aaa, "VALI %s|%d", username, axlevel);
913         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
914         free(aaa);
915         return ret;
916 }
917
918
919 /* EINF */
920 int CtdlIPCSetRoomInfo(int for_real, const char *info, char *cret)
921 {
922         char aaa[16];
923
924         if (!cret) return -1;
925         if (!info) return -1;
926
927         sprintf(aaa, "EINF %d", for_real);
928         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
929 }
930
931
932 /* LIST */
933 int CtdlIPCUserListing(char **listing, char *cret)
934 {
935         size_t bytes;
936
937         if (!cret) return -1;
938         if (!listing) return -1;
939         if (*listing) return -1;
940
941         return CtdlIPCGenericCommand("LIST", NULL, 0, listing, &bytes, cret);
942 }
943
944
945 /* REGI */
946 int CtdlIPCSetRegistration(const char *info, char *cret)
947 {
948         if (!cret) return -1;
949         if (!info) return -1;
950
951         return CtdlIPCGenericCommand("REGI", info, strlen(info),
952                         NULL, NULL, cret);
953 }
954
955
956 /* CHEK */
957 int CtdlIPCMiscCheck(struct ctdlipcmisc *chek, char *cret)
958 {
959         register int ret;
960
961         if (!cret) return -1;
962         if (!chek) return -1;
963
964         ret = CtdlIPCGenericCommand("CHEK", NULL, 0, NULL, NULL, cret);
965         if (ret / 100 == 2) {
966                 chek->newmail = extract_long(cret, 0);
967                 chek->needregis = extract_int(cret, 1);
968                 chek->needvalid = extract_int(cret, 2);
969         }
970         return ret;
971 }
972
973
974 /* DELF */
975 int CtdlIPCDeleteFile(const char *filename, char *cret)
976 {
977         register int ret;
978         char *aaa;
979
980         if (!cret) return -2;
981         if (!filename) return -2;
982         
983         aaa = (char *)malloc(strlen(filename) + 6);
984         if (!aaa) return -1;
985
986         sprintf(aaa, "DELF %s", filename);
987         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
988         free(aaa);
989         return ret;
990 }
991
992
993 /* MOVF */
994 int CtdlIPCMoveFile(const char *filename, const char *destroom, char *cret)
995 {
996         register int ret;
997         char *aaa;
998
999         if (!cret) return -2;
1000         if (!filename) return -2;
1001         if (!destroom) return -2;
1002
1003         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1004         if (!aaa) return -1;
1005
1006         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1007         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1008         free(aaa);
1009         return ret;
1010 }
1011
1012
1013 /* NETF */
1014 int CtdlIPCNetSendFile(const char *filename, const char *destnode, char *cret)
1015 {
1016         register int ret;
1017         char *aaa;
1018
1019         if (!cret) return -2;
1020         if (!filename) return -2;
1021         if (!destnode) return -2;
1022
1023         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1024         if (!aaa) return -1;
1025
1026         sprintf(aaa, "NETF %s|%s", filename, destnode);
1027         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1028         free(aaa);
1029         return ret;
1030 }
1031
1032
1033 /* RWHO */
1034 int CtdlIPCOnlineUsers(char **listing, time_t *stamp, char *cret)
1035 {
1036         register int ret;
1037         size_t bytes;
1038
1039         if (!cret) return -1;
1040         if (!listing) return -1;
1041         if (*listing) return -1;
1042
1043         *stamp = CtdlIPCServerTime(cret);
1044         if (!*stamp)
1045                 *stamp = time(NULL);
1046         ret = CtdlIPCGenericCommand("RWHO", NULL, 0, listing, &bytes, cret);
1047         return ret;
1048 }
1049
1050
1051 /* OPEN */
1052 int CtdlIPCFileDownload(const char *filename, void **buf, char *cret)
1053 {
1054         register int ret;
1055         size_t bytes;
1056         time_t last_mod;
1057         char mimetype[SIZ];
1058         char *aaa;
1059
1060         if (!cret) return -2;
1061         if (!filename) return -2;
1062         if (!buf) return -2;
1063         if (*buf) return -2;
1064         if (download_in_progress) return -2;
1065
1066         aaa = (char *)malloc(strlen(filename) + 6);
1067         if (!aaa) return -1;
1068
1069         sprintf(aaa, "OPEN %s", filename);
1070         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1071         free(aaa);
1072         /* FIXME: Possible race condition */
1073         if (ret / 100 == 2) {
1074                 download_in_progress = 1;
1075                 bytes = extract_long(cret, 0);
1076                 last_mod = extract_int(cret, 1);
1077                 extract(mimetype, cret, 2);
1078                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1079                 ret = CtdlIPCEndDownload(cret);
1080                 if (ret / 100 == 2)
1081                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1082                                         filename, mimetype);
1083         }
1084         return ret;
1085 }
1086
1087
1088 /* OPNA */
1089 int CtdlIPCAttachmentDownload(long msgnum, const char *part, void **buf,
1090                 char *cret)
1091 {
1092         register int ret;
1093         size_t bytes;
1094         time_t last_mod;
1095         char filename[SIZ];
1096         char mimetype[SIZ];
1097         char *aaa;
1098
1099         if (!cret) return -2;
1100         if (!buf) return -2;
1101         if (*buf) return -2;
1102         if (!part) return -2;
1103         if (!msgnum) return -2;
1104         if (download_in_progress) return -2;
1105
1106         aaa = (char *)malloc(strlen(part) + 17);
1107         if (!aaa) return -1;
1108
1109         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1110         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1111         free(aaa);
1112         /* FIXME: Possible race condition */
1113         if (ret / 100 == 2) {
1114                 download_in_progress = 1;
1115                 bytes = extract_long(cret, 0);
1116                 last_mod = extract_int(cret, 1);
1117                 extract(mimetype, cret, 2);
1118                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1119                 ret = CtdlIPCEndDownload(cret);
1120                 if (ret / 100 == 2)
1121                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1122                                         filename, mimetype);
1123         }
1124         return ret;
1125 }
1126
1127
1128 /* OIMG */
1129 int CtdlIPCImageDownload(const char *filename, void **buf, char *cret)
1130 {
1131         register int ret;
1132         size_t bytes;
1133         time_t last_mod;
1134         char mimetype[SIZ];
1135         char *aaa;
1136
1137         if (!cret) return -1;
1138         if (!buf) return -1;
1139         if (*buf) return -1;
1140         if (!filename) return -1;
1141         if (download_in_progress) return -1;
1142
1143         aaa = (char *)malloc(strlen(filename) + 6);
1144         if (!aaa) return -1;
1145
1146         sprintf(aaa, "OIMG %s", filename);
1147         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1148         free(aaa);
1149         /* FIXME: Possible race condition */
1150         if (ret / 100 == 2) {
1151                 download_in_progress = 1;
1152                 bytes = extract_long(cret, 0);
1153                 last_mod = extract_int(cret, 1);
1154                 extract(mimetype, cret, 2);
1155                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1156                 ret = CtdlIPCEndDownload(cret);
1157                 if (ret / 100 == 2)
1158                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1159                                         filename, mimetype);
1160         }
1161         return ret;
1162 }
1163
1164
1165 /* UOPN */
1166 int CtdlIPCFileUpload(const char *filename, const char *comment, void *buf,
1167                 size_t bytes, char *cret)
1168 {
1169         register int ret;
1170         char *aaa;
1171
1172         if (!cret) return -1;
1173         if (!filename) return -1;
1174         if (!comment) return -1;
1175         if (upload_in_progress) return -1;
1176
1177         aaa = (char *)malloc(strlen(filename) + strlen(comment) + 7);
1178         if (!aaa) return -1;
1179
1180         sprintf(aaa, "UOPN %s|%s", filename, comment);
1181         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1182         free(aaa);
1183         /* FIXME: Possible race condition */
1184         if (ret / 100 == 2)
1185                 upload_in_progress = 1;
1186         ret = CtdlIPCWriteUpload(buf, bytes, cret);
1187         ret = CtdlIPCEndUpload(cret);
1188         return ret;
1189 }
1190
1191
1192 /* UIMG */
1193 int CtdlIPCImageUpload(int for_real, const char *filename, size_t bytes,
1194                 char *cret)
1195 {
1196         register int ret;
1197         char *aaa;
1198
1199         if (!cret) return -1;
1200         if (!filename) return -1;
1201         if (upload_in_progress) return -1;
1202
1203         aaa = (char *)malloc(strlen(filename) + 17);
1204         if (!aaa) return -1;
1205
1206         sprintf(aaa, "UIMG %d|%s", for_real, filename);
1207         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1208         free(aaa);
1209         /* FIXME: Possible race condition */
1210         if (ret / 100 == 2)
1211                 upload_in_progress = 1;
1212         return ret;
1213 }
1214
1215
1216 /* QUSR */
1217 int CtdlIPCQueryUsername(const char *username, char *cret)
1218 {
1219         register int ret;
1220         char *aaa;
1221
1222         if (!cret) return -2;
1223         if (!username) return -2;
1224
1225         aaa = (char *)malloc(strlen(username) + 6);
1226         if (!aaa) return -1;
1227
1228         sprintf(aaa, "QUSR %s", username);
1229         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1230         free(aaa);
1231         return ret;
1232 }
1233
1234
1235 /* LFLR */
1236 int CtdlIPCFloorListing(char **listing, char *cret)
1237 {
1238         size_t bytes;
1239
1240         if (!cret) return -2;
1241         if (!listing) return -2;
1242         if (*listing) return -2;
1243
1244         return CtdlIPCGenericCommand("LFLR", NULL, 0, listing, &bytes, cret);
1245 }
1246
1247
1248 /* CFLR */
1249 int CtdlIPCCreateFloor(int for_real, const char *name, char *cret)
1250 {
1251         register int ret;
1252         char *aaa;
1253
1254         if (!cret) return -2;
1255         if (!name) return -2;
1256
1257         aaa = (char *)malloc(strlen(name) + 17);
1258         if (!aaa) return -1;
1259
1260         sprintf(aaa, "CFLR %s|%d", name, for_real);
1261         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1262         free(aaa);
1263         return ret;
1264 }
1265
1266
1267 /* KFLR */
1268 int CtdlIPCDeleteFloor(int for_real, int floornum, char *cret)
1269 {
1270         char aaa[27];
1271
1272         if (!cret) return -1;
1273         if (floornum < 0) return -1;
1274
1275         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1276         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1277 }
1278
1279
1280 /* EFLR */
1281 int CtdlIPCEditFloor(int floornum, const char *floorname, char *cret)
1282 {
1283         register int ret;
1284         char *aaa;
1285
1286         if (!cret) return -2;
1287         if (!floorname) return -2;
1288         if (floornum < 0) return -2;
1289
1290         aaa = (char *)malloc(strlen(floorname) + 17);
1291         if (!aaa) return -1;
1292
1293         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1294         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1295         free(aaa);
1296         return ret;
1297 }
1298
1299
1300 /* IDEN */
1301 int CtdlIPCIdentifySoftware(int developerid, int clientid, int revision,
1302                 const char *software_name, const char *hostname, char *cret)
1303 {
1304         register int ret;
1305         char *aaa;
1306
1307         if (developerid < 0) return -2;
1308         if (clientid < 0) return -2;
1309         if (revision < 0) return -2;
1310         if (!software_name) return -2;
1311         if (!hostname) return -2;
1312
1313         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1314         if (!aaa) return -1;
1315
1316         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1317                         revision, software_name, hostname);
1318         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1319         free(aaa);
1320         return ret;
1321 }
1322
1323
1324 /* SEXP */
1325 int CtdlIPCSendInstantMessage(const char *username, const char *text,
1326                 char *cret)
1327 {
1328         register int ret;
1329         char *aaa;
1330
1331         if (!cret) return -2;
1332         if (!username) return -2;
1333
1334         aaa = (char *)malloc(strlen(username) + 8);
1335         if (!aaa) return -1;
1336
1337         if (text) {
1338                 sprintf(aaa, "SEXP %s|-", username);
1339                 ret = CtdlIPCGenericCommand(aaa, text, strlen(text),
1340                                 NULL, NULL, cret);
1341         } else {
1342                 sprintf(aaa, "SEXP %s||", username);
1343                 ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1344         }
1345         free(aaa);
1346         return ret;
1347 }
1348
1349
1350 /* GEXP */
1351 int CtdlIPCGetInstantMessage(char **listing, char *cret)
1352 {
1353         size_t bytes;
1354
1355         if (!cret) return -2;
1356         if (!listing) return -2;
1357         if (*listing) return -2;
1358
1359         return CtdlIPCGenericCommand("GEXP", NULL, 0, listing, &bytes, cret);
1360 }
1361
1362
1363 /* DEXP */
1364 /* mode is 0 = enable, 1 = disable, 2 = status */
1365 int CtdlIPCEnableInstantMessageReceipt(int mode, char *cret)
1366 {
1367         char aaa[16];
1368
1369         if (!cret) return -2;
1370
1371         sprintf(aaa, "DEXP %d", mode);
1372         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1373 }
1374
1375
1376 /* EBIO */
1377 int CtdlIPCSetBio(char *bio, char *cret)
1378 {
1379         if (!cret) return -2;
1380         if (!bio) return -2;
1381
1382         return CtdlIPCGenericCommand("EBIO", bio, strlen(bio),
1383                         NULL, NULL, cret);
1384 }
1385
1386
1387 /* RBIO */
1388 int CtdlIPCGetBio(const char *username, char **listing, char *cret)
1389 {
1390         register int ret;
1391         size_t bytes;
1392         char *aaa;
1393
1394         if (!cret) return -2;
1395         if (!username) return -2;
1396         if (!listing) return -2;
1397         if (*listing) return -2;
1398
1399         aaa = (char *)malloc(strlen(username) + 6);
1400         if (!aaa) return -1;
1401
1402         sprintf(aaa, "RBIO %s", username);
1403         ret = CtdlIPCGenericCommand(aaa, NULL, 0, listing, &bytes, cret);
1404         free(aaa);
1405         return ret;
1406 }
1407
1408
1409 /* LBIO */
1410 int CtdlIPCListUsersWithBios(char **listing, char *cret)
1411 {
1412         size_t bytes;
1413
1414         if (!cret) return -2;
1415         if (!listing) return -2;
1416         if (*listing) return -2;
1417
1418         return CtdlIPCGenericCommand("LBIO", NULL, 0, listing, &bytes, cret);
1419 }
1420
1421
1422 /* STEL */
1423 int CtdlIPCStealthMode(int mode, char *cret)
1424 {
1425         char aaa[16];
1426
1427         if (!cret) return -1;
1428
1429         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1430         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1431 }
1432
1433
1434 /* TERM */
1435 int CtdlIPCTerminateSession(int sid, char *cret)
1436 {
1437         char aaa[16];
1438
1439         if (!cret) return -1;
1440
1441         sprintf(aaa, "TERM %d", sid);
1442         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1443 }
1444
1445
1446 /* DOWN */
1447 int CtdlIPCTerminateServerNow(char *cret)
1448 {
1449         if (!cret) return -1;
1450
1451         return CtdlIPCGenericCommand("DOWN", NULL, 0, NULL, NULL, cret);
1452 }
1453
1454
1455 /* SCDN */
1456 int CtdlIPCTerminateServerScheduled(int mode, char *cret)
1457 {
1458         char aaa[16];
1459
1460         if (!cret) return -1;
1461
1462         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1463         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1464 }
1465
1466
1467 /* EMSG */
1468 int CtdlIPCEnterSystemMessage(const char *filename, const char *text,
1469                 char *cret)
1470 {
1471         register int ret;
1472         char *aaa;
1473
1474         if (!cret) return -2;
1475         if (!text) return -2;
1476         if (!filename) return -2;
1477
1478         aaa = (char *)malloc(strlen(filename) + 6);
1479         if (!aaa) return -1;
1480
1481         sprintf(aaa, "EMSG %s", filename);
1482         ret = CtdlIPCGenericCommand(aaa, text, strlen(text), NULL, NULL, cret);
1483         free(aaa);
1484         return ret;
1485 }
1486
1487
1488 /* HCHG */
1489 int CtdlIPCChangeHostname(const char *hostname, char *cret)
1490 {
1491         register int ret;
1492         char *aaa;
1493
1494         if (!cret) return -2;
1495         if (!hostname) return -2;
1496
1497         aaa = (char *)malloc(strlen(hostname) + 6);
1498         if (!aaa) return -1;
1499
1500         sprintf(aaa, "HCHG %s", hostname);
1501         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1502         free(aaa);
1503         return ret;
1504 }
1505
1506
1507 /* RCHG */
1508 int CtdlIPCChangeRoomname(const char *roomname, char *cret)
1509 {
1510         register int ret;
1511         char *aaa;
1512
1513         if (!cret) return -2;
1514         if (!roomname) return -2;
1515
1516         aaa = (char *)malloc(strlen(roomname) + 6);
1517         if (!aaa) return -1;
1518
1519         sprintf(aaa, "RCHG %s", roomname);
1520         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1521         free(aaa);
1522         return ret;
1523 }
1524
1525
1526 /* UCHG */
1527 int CtdlIPCChangeUsername(const char *username, char *cret)
1528 {
1529         register int ret;
1530         char *aaa;
1531
1532         if (!cret) return -2;
1533         if (!username) return -2;
1534
1535         aaa = (char *)malloc(strlen(username) + 6);
1536         if (!aaa) return -1;
1537
1538         sprintf(aaa, "UCHG %s", username);
1539         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1540         free(aaa);
1541         return ret;
1542 }
1543
1544
1545 /* TIME */
1546 /* This function returns the actual server time reported, or 0 if error */
1547 time_t CtdlIPCServerTime(char *cret)
1548 {
1549         register time_t tret;
1550         register int ret;
1551
1552         ret = CtdlIPCGenericCommand("TIME", NULL, 0, NULL, NULL, cret);
1553         if (ret / 100 == 2) {
1554                 tret = extract_long(cret, 0);
1555         } else {
1556                 tret = 0L;
1557         }
1558         return tret;
1559 }
1560
1561
1562 /* AGUP */
1563 int CtdlIPCAideGetUserParameters(const char *who,
1564                                  struct usersupp **uret, char *cret)
1565 {
1566         register int ret;
1567         char *aaa;
1568
1569         if (!cret) return -2;
1570         if (!uret) return -2;
1571         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1572         if (!*uret) return -1;
1573
1574         aaa = (char *)malloc(strlen(uret[0]->fullname) + 6);
1575         if (!aaa) return -1;
1576
1577         sprintf(aaa, "AGUP %s", who);
1578         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1579         if (ret / 100 == 2) {
1580                 extract(uret[0]->fullname, cret, 0);
1581                 extract(uret[0]->password, cret, 1);
1582                 uret[0]->flags = extract_int(cret, 2);
1583                 uret[0]->timescalled = extract_long(cret, 3);
1584                 uret[0]->posted = extract_long(cret, 4);
1585                 uret[0]->axlevel = extract_int(cret, 5);
1586                 uret[0]->usernum = extract_long(cret, 6);
1587                 uret[0]->lastcall = extract_long(cret, 7);
1588                 uret[0]->USuserpurge = extract_int(cret, 8);
1589         }
1590         free(aaa);
1591         return ret;
1592 }
1593
1594
1595 /* ASUP */
1596 int CtdlIPCAideSetUserParameters(const struct usersupp *uret, char *cret)
1597 {
1598         register int ret;
1599         char *aaa;
1600
1601         if (!cret) return -2;
1602         if (!uret) return -2;
1603
1604         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1605         if (!aaa) return -1;
1606
1607         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1608                         uret->fullname, uret->password, uret->flags,
1609                         uret->timescalled, uret->posted, uret->axlevel,
1610                         uret->usernum, uret->lastcall, uret->USuserpurge);
1611         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1612         free(aaa);
1613         return ret;
1614 }
1615
1616
1617 /* GPEX */
1618 /* which is 0 = room, 1 = floor, 2 = site */
1619 int CtdlIPCGetMessageExpirationPolicy(int which, char *cret)
1620 {
1621         static char *proto[] = {"room", "floor", "site"};
1622         char aaa[11];
1623
1624         if (!cret) return -2;
1625         if (which < 0 || which > 2) return -2;
1626         
1627         sprintf(aaa, "GPEX %s", proto[which]);
1628         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1629 }
1630
1631
1632 /* SPEX */
1633 /* which is 0 = room, 1 = floor, 2 = site */
1634 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1635 int CtdlIPCSetMessageExpirationPolicy(int which, int policy, int value,
1636                 char *cret)
1637 {
1638         char aaa[38];
1639
1640         if (!cret) return -2;
1641         if (which < 0 || which > 2) return -2;
1642         if (policy < 0 || policy > 3) return -2;
1643         if (policy >= 2 && value < 1) return -2;
1644
1645         sprintf(aaa, "SPEX %d|%d|%d", which, policy, value);
1646         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1647 }
1648
1649
1650 /* CONF GET */
1651 int CtdlGetSystemConfig(char **listing, char *cret)
1652 {
1653         size_t bytes;
1654
1655         if (!cret) return -2;
1656         if (!listing) return -2;
1657         if (*listing) return -2;
1658
1659         return CtdlIPCGenericCommand("CONF GET", NULL, 0,
1660                         listing, &bytes, cret);
1661 }
1662
1663
1664 /* CONF SET */
1665 int CtdlSetSystemConfig(const char *listing, char *cret)
1666 {
1667         if (!cret) return -2;
1668         if (!listing) return -2;
1669
1670         return CtdlIPCGenericCommand("CONF SET", listing, strlen(listing),
1671                         NULL, NULL, cret);
1672 }
1673
1674
1675 /* MMOD */
1676 int CtdlIPCModerateMessage(long msgnum, int level, char *cret)
1677 {
1678         char aaa[27];
1679
1680         if (!cret) return -2;
1681         if (!msgnum) return -2;
1682
1683         sprintf(aaa, "MMOD %ld|%d", msgnum, level);
1684         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1685 }
1686
1687
1688 /* REQT */
1689 int CtdlIPCRequestClientLogout(int session, char *cret)
1690 {
1691         char aaa[16];
1692
1693         if (!cret) return -2;
1694         if (session < 0) return -2;
1695
1696         sprintf(aaa, "REQT %d", session);
1697         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1698 }
1699
1700
1701 /* SEEN */
1702 int CtdlIPCSetMessageSeen(long msgnum, int seen, char *cret)
1703 {
1704         char aaa[27];
1705
1706         if (!cret) return -2;
1707         if (msgnum < 0) return -2;
1708
1709         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1710         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1711 }
1712
1713
1714 /* STLS */
1715 int CtdlIPCStartEncryption(char *cret)
1716 {
1717         return CtdlIPCGenericCommand("STLS", NULL, 0, NULL, NULL, cret);
1718 }
1719
1720
1721 /* QDIR */
1722 int CtdlIPCDirectoryLookup(const char *address, char *cret)
1723 {
1724         char *aaa;
1725
1726         if (!address) return -2;
1727         if (!cret) return -2;
1728
1729         aaa = (char *)malloc(strlen(address) + 6);
1730         if (!aaa) return -1;
1731
1732         sprintf(aaa, "QDIR %s", address);
1733         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1734 }
1735
1736
1737 /*
1738  * Not implemented:
1739  * 
1740  * CHAT
1741  * ETLS
1742  * EXPI
1743  * GTLS
1744  * IGAB
1745  * IPGM
1746  * MSG3
1747  * MSG4
1748  * NDOP
1749  * NETP
1750  * NUOP
1751  * SMTP
1752  */
1753
1754
1755 /* ************************************************************************** */
1756 /*             Stuff below this line is not for public consumption            */
1757 /* ************************************************************************** */
1758
1759
1760 inline void netio_lock(void)
1761 {
1762 #ifdef THREADED_CLIENT
1763         pthread_mutex_lock(&rwlock);
1764 #endif
1765 }
1766
1767
1768 inline void netio_unlock(void)
1769 {
1770 #ifdef THREADED_CLIENT
1771         pthread_mutex_unlock(&rwlock);
1772 #endif
1773 }
1774
1775
1776 /* Read a listing from the server up to 000.  Append to dest if it exists */
1777 char *CtdlIPCReadListing(char *dest)
1778 {
1779         size_t length = 0;
1780         size_t linelength;
1781         char *ret;
1782         char aaa[SIZ];
1783
1784         ret = dest;
1785         if (ret != NULL) {
1786                 length = strlen(ret);
1787         }
1788         else {
1789                 ret = strdup("");
1790                 length = 0;
1791         }
1792
1793         while (serv_gets(aaa), strcmp(aaa, "000")) {
1794                 linelength = strlen(aaa);
1795                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
1796                 if (ret) {
1797                         strcpy(&ret[length], aaa);
1798                         length += linelength;
1799                         strcpy(&ret[length++], "\n");
1800                 }
1801         }
1802
1803         return(ret);
1804 }
1805
1806
1807 /* Send a listing to the server; generate the ending 000. */
1808 int CtdlIPCSendListing(const char *listing)
1809 {
1810         char *text;
1811
1812         text = (char *)malloc(strlen(listing) + 6);
1813         if (text) {
1814                 strcpy(text, listing);
1815                 while (text[strlen(text) - 1] == '\n')
1816                         text[strlen(text) - 1] = '\0';
1817                 strcat(text, "\n000");
1818                 serv_puts(text);
1819                 free(text);
1820                 text = NULL;
1821         } else {
1822                 /* Malloc failed but we are committed to send */
1823                 /* This may result in extra blanks at the bottom */
1824                 serv_puts(text);
1825                 serv_puts("000");
1826         }
1827         return 0;
1828 }
1829
1830
1831 /* Partial read of file from server */
1832 size_t CtdlIPCPartialRead(void **buf, size_t offset, size_t bytes, char *cret)
1833 {
1834         register size_t len = 0;
1835         char aaa[SIZ];
1836
1837         if (!buf) return -1;
1838         if (!cret) return -1;
1839         if (bytes < 1) return -1;
1840         if (offset < 0) return -1;
1841
1842         netio_lock();
1843         sprintf(aaa, "READ %d|%d", offset, bytes);
1844         serv_puts(aaa);
1845         serv_gets(aaa);
1846         if (aaa[0] != '6')
1847                 strcpy(cret, &aaa[4]);
1848         else {
1849                 len = extract_long(&aaa[4], 0);
1850                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
1851                 if (*buf) {
1852                         /* I know what I'm doing */
1853                         serv_read((char *)&buf[offset], len);
1854                 } else {
1855                         /* We have to read regardless */
1856                         serv_read(aaa, len);
1857                         len = -1;
1858                 }
1859         }
1860         netio_unlock();
1861         return len;
1862 }
1863
1864
1865 /* CLOS */
1866 int CtdlIPCEndDownload(char *cret)
1867 {
1868         register int ret;
1869
1870         if (!cret) return -2;
1871         if (!download_in_progress) return -2;
1872
1873         ret = CtdlIPCGenericCommand("CLOS", NULL, 0, NULL, NULL, cret);
1874         if (ret / 100 == 2)
1875                 download_in_progress = 0;
1876         return ret;
1877 }
1878
1879
1880 /* MSGP */
1881 int CtdlIPCSpecifyPreferredFormats(char *cret, char *formats) {
1882         register int ret;
1883         char cmd[SIZ];
1884         
1885         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
1886         ret = CtdlIPCGenericCommand(cmd, NULL, 0, NULL, NULL, cret);
1887         return ret;
1888 }
1889
1890
1891
1892 /* READ */
1893 int CtdlIPCReadDownload(void **buf, size_t bytes, char *cret)
1894 {
1895         register size_t len;
1896
1897         if (!cret) return -1;
1898         if (!buf) return -1;
1899         if (*buf) return -1;
1900         if (!download_in_progress) return -1;
1901
1902         len = 0;
1903         while (len < bytes) {
1904                 len = CtdlIPCPartialRead(buf, len, 4096, cret);
1905                 if (len == -1) {
1906                         free(*buf);
1907                         return 0;
1908                 }
1909         }
1910         return len;
1911 }
1912
1913
1914 /* UCLS */
1915 int CtdlIPCEndUpload(char *cret)
1916 {
1917         register int ret;
1918
1919         if (!cret) return -1;
1920         if (!upload_in_progress) return -1;
1921
1922         ret = CtdlIPCGenericCommand("UCLS", NULL, 0, NULL, NULL, cret);
1923         if (ret / 100 == 2)
1924                 upload_in_progress = 0;
1925         return ret;
1926 }
1927
1928
1929 /* WRIT */
1930 int CtdlIPCWriteUpload(void *buf, size_t bytes, char *cret)
1931 {
1932         register int ret = -1;
1933         register size_t offset;
1934         char aaa[SIZ];
1935
1936         if (!cret) return -1;
1937         if (!buf) return -1;
1938         if (bytes < 1) return -1;
1939
1940         offset = 0;
1941         while (offset < bytes) {
1942                 sprintf(aaa, "WRIT %d", bytes - offset);
1943                 serv_puts(aaa);
1944                 serv_gets(aaa);
1945                 strcpy(cret, &aaa[4]);
1946                 ret = atoi(aaa);
1947                 if (aaa[0] == '7') {
1948                         register size_t to_write;
1949
1950                         to_write = extract_long(&aaa[4], 0);
1951                         serv_write(buf + offset, to_write);
1952                         offset += to_write;
1953                 } else {
1954                         break;
1955                 }
1956         }
1957         return ret;
1958 }
1959
1960
1961 /*
1962  * Generic command method.  This method should handle any server command
1963  * except for CHAT.  It takes the following arguments:
1964  *
1965  * command              Preformatted command to send to server
1966  * to_send              A text or binary file to send to server
1967  *                      (only sent if server requests it)
1968  * bytes_to_send        The number of bytes in to_send (required if
1969  *                      sending binary, optional if sending listing)
1970  * to_receive           Pointer to a NULL pointer, if the server
1971  *                      sends text or binary we will allocate memory
1972  *                      for the file and stuff it here
1973  * bytes_to_receive     If a file is received, we will store its
1974  *                      byte count here
1975  * proto_response       The protocol response.  Caller must provide
1976  *                      this buffer and ensure that it is at least
1977  *                      128 bytes in length.
1978  *
1979  * This function returns a number equal to the protocol response number,
1980  * -1 if an internal error occurred, -2 if caller provided bad values,
1981  * or 0 - the protocol response number if bad values were found during
1982  * the protocol exchange.
1983  * It stores the protocol response string (minus the number) in 
1984  * protocol_response as described above.  Some commands send additional
1985  * data in this string.
1986  */
1987 int CtdlIPCGenericCommand(const char *command, const char *to_send,
1988                 size_t bytes_to_send, char **to_receive, 
1989                 size_t *bytes_to_receive, char *proto_response)
1990 {
1991         char buf[SIZ];
1992         register int ret;
1993
1994         if (!command) return -2;
1995         if (!proto_response) return -2;
1996
1997         netio_lock();
1998         serv_puts((char *)command);
1999         while (1) {
2000                 serv_gets(proto_response);
2001                 if (proto_response[3] == '*')
2002                         express_msgs = 1;
2003                 ret = atoi(proto_response);
2004                 strcpy(proto_response, &proto_response[4]);
2005                 switch (ret / 100) {
2006                 default:                        /* Unknown, punt */
2007                 case 2:                         /* OK */
2008                 case 3:                         /* MORE_DATA */
2009                 case 5:                         /* ERROR */
2010                         /* Don't need to do anything */
2011                         break;
2012                 case 1:                         /* LISTING_FOLLOWS */
2013                         if (to_receive && !*to_receive && bytes_to_receive) {
2014                                 *to_receive = CtdlIPCReadListing(NULL);
2015                         } else { /* Drain */
2016                                 while (serv_gets(buf), strcmp(buf, "000")) ;
2017                                 ret = -ret;
2018                         }
2019                         break;
2020                 case 4:                         /* SEND_LISTING */
2021                         if (to_send) {
2022                                 CtdlIPCSendListing(to_send);
2023                         } else {
2024                                 /* No listing given, fake it */
2025                                 serv_puts("000");
2026                                 ret = -ret;
2027                         }
2028                         break;
2029                 case 6:                         /* BINARY_FOLLOWS */
2030                         if (to_receive && !*to_receive && bytes_to_receive) {
2031                                 *bytes_to_receive =
2032                                         extract_long(proto_response, 0);
2033                                 *to_receive = (char *)
2034                                         malloc((size_t)*bytes_to_receive);
2035                                 if (!*to_receive) {
2036                                         ret = -1;
2037                                 } else {
2038                                         serv_read(*to_receive,
2039                                                         *bytes_to_receive);
2040                                 }
2041                         } else {
2042                                 /* Drain */
2043                                 size_t drain;
2044
2045                                 drain = extract_long(proto_response, 0);
2046                                 while (drain > SIZ) {
2047                                         serv_read(buf, SIZ);
2048                                         drain -= SIZ;
2049                                 }
2050                                 serv_read(buf, drain);
2051                                 ret = -ret;
2052                         }
2053                         break;
2054                 case 7:                         /* SEND_BINARY */
2055                         if (to_send && bytes_to_send) {
2056                                 serv_write((char *)to_send, bytes_to_send);
2057                         } else if (bytes_to_send) {
2058                                 /* Fake it, send nulls */
2059                                 size_t fake;
2060
2061                                 fake = bytes_to_send;
2062                                 memset(buf, '\0', SIZ);
2063                                 while (fake > SIZ) {
2064                                         serv_write(buf, SIZ);
2065                                         fake -= SIZ;
2066                                 }
2067                                 serv_write(buf, fake);
2068                                 ret = -ret;
2069                         } /* else who knows?  DANGER WILL ROBINSON */
2070                         break;
2071                 case 8:                         /* START_CHAT_MODE */
2072                         if (!strncasecmp(command, "CHAT", 4)) {
2073                                 /* Don't call chatmode with generic! */
2074                                 serv_puts("/quit");
2075                                 ret = -ret;
2076                         } else {
2077                                 /* In this mode we send then receive listing */
2078                                 if (to_send) {
2079                                         CtdlIPCSendListing(to_send);
2080                                 } else {
2081                                         /* No listing given, fake it */
2082                                         serv_puts("000");
2083                                         ret = -ret;
2084                                 }
2085                                 if (to_receive && !*to_receive
2086                                                 && bytes_to_receive) {
2087                                         *to_receive = CtdlIPCReadListing(NULL);
2088                                 } else { /* Drain */
2089                                         while (serv_gets(buf),
2090                                                         strcmp(buf, "000")) ;
2091                                         ret = -ret;
2092                                 }
2093                         }
2094                         break;
2095                 case 9:                         /* ASYNC_MSG */
2096                         /* CtdlIPCDoAsync(ret, proto_response); */
2097                         free(CtdlIPCReadListing(NULL)); /* STUB FIXME */
2098                         break;
2099                 }
2100                 if (ret / 100 != 9)
2101                         break;
2102         }
2103         netio_unlock();
2104         return ret;
2105 }