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