-Copyright: (C) 1987-2017 Citadel development team; GPL V3
+Copyright: (C) 1987-2018 Citadel development team; GPL V3
Copyright for CRC16: (C) 2002-2003 Indigo Systems Corporation;
Artistic license + Non advertising clause
* In addition, as a special exception, we hereby declare that our
favorite type of software is called "open source" -- NOT "free
software" -- and that our favorite operating system is called
- "Linux" -- NOT "GNU/Linux." We officially reject and denounce
- Richard Stallman's linguistic fascism.
+ "Linux" -- NOT "GNU/Linux".
* In addition, as a special exception, the copyright holders give
permission to link the code of portions of this program with the
This code was written by Colin Plumb in 1993, no copyright is claimed.
This code is in the public domain; do with it what you wish.
-RFC 2739 openldap schema license:
-
- This document and translations of it may be copied and furnished to
- others, and derivative works that comment on or otherwise explain it
- or assist in its implementation may be prepared, copied, published
- and distributed, in whole or in part, without restriction of any
- kind, provided that the above copyright notice and this paragraph are
- included on all such copies and derivative works. However, this
- document itself may not be modified in any way, such as by removing
- the copyright notice or references to the Internet Society or other
- Internet organizations, except as needed for the purpose of
- developing Internet standards in which case the procedures for
- copyrights defined in the Internet Standards process must be
- followed, or as required to translate it into languages other than
- English.
-
- The limited permissions granted above are perpetual and will not be
- revoked by the Internet Society or its successors or assigns.
-
- This document and the information contained herein is provided on an
- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
UTIL_TARGETS=citmail$(EXEEXT) sendcommand$(EXEEXT)
-UTILBIN_TARGETS= base64$(EXEEXT) setup$(EXEEXT) \
+UTILBIN_TARGETS= setup$(EXEEXT) \
chkpw$(EXEEXT) chkpwd$(EXEEXT) \
- msgform$(EXEEXT) \
ctdlmigrate$(EXEEXT)
-
ACLOCAL=@ACLOCAL@
AUTOCONF=@AUTOCONF@
chkpwd_LIBS=@chkpwd_LIBS@
SOURCES=utils/citmail.c \
- utils/setup.c utils/msgform.c utils/chkpw.c \
+ utils/setup.c utils/chkpw.c \
utils/sendcommand.c \
- utils/ctdlmigrate.c utils/base64.c utils/chkpwd.c \
+ utils/ctdlmigrate.c utils/chkpwd.c \
utillib/citadel_dirs.c \
citserver.c clientsocket.c config.c control.c $(DATABASE) \
domain.c serv_extensions.c genstamp.c \
$(CC) utils/sendcommand.o utillib/citadel_dirs.o \
$(LIBOBJS) $(LDFLAGS) -o sendcommand$(EXEEXT) $(LIBS)
-base64$(EXEEXT): utils/base64.o
- $(CC) utils/base64.o $(LDFLAGS) -o base64$(EXEEXT)
-
-msgform$(EXEEXT): utils/msgform.o
- $(CC) utils/msgform.o $(LDFLAGS) -o msgform$(EXEEXT)
-
.PHONY: install-data install-doc install-exec clean cleaner distclean
install-locale:
+++ /dev/null
-Every client for the Citadel system is capable of identifying itself with
-a developer code, client code, and client version. Here are the known values
-for these fields.
-
-
-Developer codes (and where known, client codes)
-
- 0 The Citadel core development team
- (See copyright.txt for the list of people involved in the project)
- Here are the client ID's we've used:
-
- Client Name Status Description
- 0 Citadel deployed Citadel text based client
- 1 lcit dead Curses-based text client
- 2 WinCit legacy Visual Basic client for Win16
- 4 WebCit deployed Web-based access to Citadel
- 5 Shaggy dead Java-powered client
- 6 Daphne dead wx multiplatform "fat" client
- 7 Shaggy (new) unknown Java-powered client
- <chilly@uncensored.citadel.org>
- 8 ctdlphp deployed PHP interface to Citadel
- 9 z-push in development Citadel backend for Z-Push
-
- 1 Brian Ledbetter <brian@shadowcom.net>
-
- Client Name Status Description
- 0 libCxClient deployed Client-side API library
- 1 Infusion deployed Groupware for Citadel
-
- 2 OST Systems, Inc.
- Matthew Scott <mscott@springfieldtech.com>
-
- Client Name Status Description
- 0 CivisLink development Python Citadel protocol library
-
- 3 Jesse Vincent <jrvincent@wesleyan.edu>
-
- 4 Brian Costello <btx@calyx.net>
-
- 5 Robert Abatecola <robert@tsgus.com>
-
- 6 Bastille <tdf@area51.v-wave.com>
-
- 7 Walden Leverich <waldenl@techsoftinc.com>
-
- 8 Michael Hampton <error@citadel.org>
-
- Client Name Status Description
- 0 libcitadel in development Client-side API library
- 1 D.O.C. dead Dave's Own Citadel look-alike
- 8 stress deployed Citadel server stress tester
- 16 Courier in development GTK+ Unix/Win32 Client
-
- 31 Stuart Cianos <scianos@alphavida.com>
-
- 69 Anticlimactic Teleservices
- Don Kimberlin (err head) <donkimberlin@hotmail.com>
-
- 71 Robert Barta <rho@cpan.org>
-
- 177 Kevin Fisher <kgf@golden.net>
-
- 226 David Given <dg@cowlark.com>
extern char file_crpt_file_csr[PATH_MAX];
extern char file_crpt_file_cer[PATH_MAX];
extern char file_chkpwd[PATH_MAX];
-extern char file_base64[PATH_MAX];
extern char file_guesstimezone[PATH_MAX];
extern void calc_dirs_n_files(int relh, int home, const char *relhome, char *ctdldir, int dbg);
this_im->conversation = NewStrBuf();
this_im->next = imlist;
imlist = this_im;
- StrBufAppendBufPlain(this_im->conversation, HKEY(
- "<html><body>\r\n"
- ), 0);
+ StrBufAppendBufPlain(this_im->conversation, HKEY("<html><body>\r\n"), 0);
}
-
/* Since it's possible for this function to get called more than once if a user is logged
* in on multiple sessions, we use the message's serial number to keep track of whether
* we've already logged it.
}
-
/*
* Retrieve instant messages
*/
free(ptr);
}
+
/*
* Asynchronously deliver instant messages
*/
cprintf("%d instant msg\n", ASYNC_MSG + ASYNC_GEXP);
}
+
/*
* Back end support function for send_instant_message() and company
*/
}
-
-
/*
* This is the back end to the instant message sending function.
* Returns the number of users to which the message was sent.
return (message_sent);
}
+
/*
* send instant messages
*/
cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
return;
}
- if (CC->fake_username[0])
+ if (CC->fake_username[0]) {
lun = CC->fake_username;
- else
+ }
+ else {
lun = CC->user.fullname;
+ }
lem = CC->cs_inet_email;
message_sent = PerformXmsgHooks(lun, lem, x_user, x_msg);
if (message_sent > 0) {
- if (!IsEmptyStr(x_msg))
+ if (!IsEmptyStr(x_msg)) {
cprintf("%d Message sent", CIT_OK);
- else
+ }
+ else {
cprintf("%d Ok to send message", CIT_OK);
- if (message_sent > 1)
+ }
+ if (message_sent > 1) {
cprintf(" to %d users", message_sent);
+ }
cprintf(".\n");
} else {
- if (CtdlGetUser(NULL, x_user))
- cprintf("%d '%s' does not exist.\n",
- ERROR + NO_SUCH_USER, x_user);
- else
- cprintf("%d '%s' is not logged in "
- "or is not accepting pages.\n",
+ if (CtdlGetUser(NULL, x_user)) {
+ cprintf("%d '%s' does not exist.\n", ERROR + NO_SUCH_USER, x_user);
+ }
+ else {
+ cprintf("%d '%s' is not logged in or is not accepting instant messages.\n",
ERROR + RESOURCE_NOT_OPEN, x_user);
+ }
}
}
-
/*
* Enter or exit paging-disabled mode
*/
FullMsgBuf = NewStrBufPlain(NULL, StrLength(im->conversation) + 100);
StrBufAppendBufPlain(FullMsgBuf, HKEY(
- "Content-type: text/html; charset=UTF-8\r\n"
- "Content-Transfer-Encoding: quoted-printable\r\n"
- "\r\n"
- ), 0);
+ "Content-type: text/html; charset=UTF-8\r\n"
+ "Content-Transfer-Encoding: quoted-printable\r\n"
+ "\r\n"
+ ), 0
+ );
StrBufAppendBuf (FullMsgBuf, MsgBuf, 0);
FreeStrBuf(&MsgBuf);
char file_crpt_file_csr[PATH_MAX]="";
char file_crpt_file_cer[PATH_MAX]="";
char file_chkpwd[PATH_MAX]="";
-char file_base64[PATH_MAX]="";
char file_guesstimezone[PATH_MAX]="";
"%schkpwd",
ctdl_utilbin_dir);
StripSlashes(file_chkpwd, 0);
- snprintf(file_base64,
- sizeof file_base64,
- "%sbase64",
- ctdl_utilbin_dir);
- StripSlashes(file_base64, 0);
snprintf(file_guesstimezone,
sizeof file_guesstimezone,
"%sguesstimezone.sh",
DBG_PRINT(file_crpt_file_csr);
DBG_PRINT(file_crpt_file_cer);
DBG_PRINT(file_chkpwd);
- DBG_PRINT(file_base64);
DBG_PRINT(file_guesstimezone);
}
+++ /dev/null
-/*
- * Encode or decode file as MIME base64 (RFC 1341)
- * Public domain by John Walker, August 11 1997
- * Modified slightly for the Citadel system, June 1999
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-
-#define TRUE 1
-#define FALSE 0
-
-#define LINELEN 72 /* Encoded line length (max 76) */
-
-typedef unsigned char byte; /* Byte type */
-
-FILE *fi; /* Input file */
-FILE *fo; /* Output file */
-static byte iobuf[256]; /* I/O buffer */
-static int iolen = 0; /* Bytes left in I/O buffer */
-static int iocp = 256; /* Character removal pointer */
-static int ateof = FALSE; /* EOF encountered */
-static byte dtable[256]; /* Encode / decode table */
-static int linelength = 0; /* Length of encoded output line */
-static char eol[] = "\r\n"; /* End of line sequence */
-static int errcheck = TRUE; /* Check decode input for errors ? */
-
-/* INBUF -- Fill input buffer with data */
-
-static int inbuf(void)
-{
- int l;
-
- if (ateof) {
- return FALSE;
- }
- l = fread(iobuf, 1, sizeof iobuf, fi); /* Read input buffer */
- if (l <= 0) {
- if (ferror(fi)) {
- exit(1);
- }
- ateof = TRUE;
- return FALSE;
- }
- iolen = l;
- iocp = 0;
- return TRUE;
-}
-
-/* INCHAR -- Return next character from input */
-
-static int inchar(void)
-{
- if (iocp >= iolen) {
- if (!inbuf()) {
- return EOF;
- }
- }
-
- return iobuf[iocp++];
-}
-
-/* OCHAR -- Output an encoded character, inserting line breaks
- where required. */
-
-static void ochar(int c)
-{
- if (linelength >= LINELEN) {
- if (fputs(eol, fo) == EOF) {
- exit(1);
- }
- linelength = 0;
- }
- if (putc(((byte) c), fo) == EOF) {
- exit(1);
- }
- linelength++;
-}
-
-/* ENCODE -- Encode binary file into base64. */
-
-static void encode(void)
-{
- int i, hiteof = FALSE;
-
- /* Fill dtable with character encodings. */
-
- for (i = 0; i < 26; i++) {
- dtable[i] = 'A' + i;
- dtable[26 + i] = 'a' + i;
- }
- for (i = 0; i < 10; i++) {
- dtable[52 + i] = '0' + i;
- }
- dtable[62] = '+';
- dtable[63] = '/';
-
- while (!hiteof) {
- byte igroup[3], ogroup[4];
- int c, n;
-
- igroup[0] = igroup[1] = igroup[2] = 0;
- for (n = 0; n < 3; n++) {
- c = inchar();
- if (c == EOF) {
- hiteof = TRUE;
- break;
- }
- igroup[n] = (byte) c;
- }
- if (n > 0) {
- ogroup[0] = dtable[igroup[0] >> 2];
- ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
- ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
- ogroup[3] = dtable[igroup[2] & 0x3F];
-
- /* Replace characters in output stream with "=" pad
- characters if fewer than three characters were
- read from the end of the input stream. */
-
- if (n < 3) {
- ogroup[3] = '=';
- if (n < 2) {
- ogroup[2] = '=';
- }
- }
- for (i = 0; i < 4; i++) {
- ochar(ogroup[i]);
- }
- }
- }
- if (fputs(eol, fo) == EOF) {
- exit(1);
- }
-}
-
-/* INSIG -- Return next significant input */
-
-static int insig(void)
-{
- int c;
-
- /*CONSTANTCONDITION*/
- while (TRUE) {
- c = inchar();
- if (c == EOF || (c > ' ')) {
- return c;
- }
- }
- /*NOTREACHED*/
-}
-
-/* DECODE -- Decode base64. */
-
-static void decode(void)
-{
- int i;
-
- for (i = 0; i < 255; i++) {
- dtable[i] = 0x80;
- }
- for (i = 'A'; i <= 'Z'; i++) {
- dtable[i] = 0 + (i - 'A');
- }
- for (i = 'a'; i <= 'z'; i++) {
- dtable[i] = 26 + (i - 'a');
- }
- for (i = '0'; i <= '9'; i++) {
- dtable[i] = 52 + (i - '0');
- }
- dtable['+'] = 62;
- dtable['/'] = 63;
- dtable['='] = 0;
-
- /*CONSTANTCONDITION*/
- while (TRUE) {
- byte a[4], b[4], o[3];
-
- for (i = 0; i < 4; i++) {
- int c = insig();
-
- if (c == EOF) {
- if (errcheck && (i > 0)) {
- fprintf(stderr, "Input file incomplete.\n");
- exit(1);
- }
- return;
- }
- if (dtable[c] & 0x80) {
- if (errcheck) {
- fprintf(stderr, "Illegal character '%c' in input file.\n", c);
- exit(1);
- }
- /* Ignoring errors: discard invalid character. */
- i--;
- continue;
- }
- a[i] = (byte) c;
- b[i] = (byte) dtable[c];
- }
- o[0] = (b[0] << 2) | (b[1] >> 4);
- o[1] = (b[1] << 4) | (b[2] >> 2);
- o[2] = (b[2] << 6) | b[3];
- i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
- if (fwrite(o, i, 1, fo) == EOF) {
- exit(1);
- }
- if (i < 3) {
- return;
- }
- }
-}
-
-/* USAGE -- Print how-to-call information. */
-
-static void usage(char *pname)
-{
- fprintf(stderr, "%s -- Encode/decode file as base64. Call:\n", pname);
- fprintf(stderr,
- " %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
- fprintf(stderr, "\n");
- fprintf(stderr, "Options:\n");
- fprintf(stderr, " -D Decode base64 encoded file\n");
- fprintf(stderr, " -E Encode file into base64\n");
- fprintf(stderr, " -N Ignore errors when decoding\n");
- fprintf(stderr, " -U Print this message\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "by John Walker\n");
- fprintf(stderr, " WWW: http://www.fourmilab.ch/\n");
-}
-
-/* Main program */
-
-int main(int argc, char *argv[])
-{
- int i, f = 0, decoding = FALSE;
- char *cp, opt;
-
- fi = stdin;
- fo = stdout;
-
- for (i = 1; i < argc; i++) {
- cp = argv[i];
- if (*cp == '-') {
- opt = *(++cp);
- if (islower(opt)) {
- opt = toupper(opt);
- }
- switch (opt) {
-
- case 'D': /* -D Decode */
- decoding = TRUE;
- break;
-
- case 'E': /* -E Encode */
- decoding = FALSE;
- break;
-
- case 'N': /* -N Suppress error checking */
- errcheck = FALSE;
- break;
-
- case 'U': /* -U Print how-to-call information */
- case '?':
- usage(argv[0]);
- return 0;
- }
- } else {
- switch (f) {
-
- /** Warning! On systems which distinguish text mode and
- binary I/O (MS-DOS, Macintosh, etc.) the modes in these
- open statements will have to be made conditional based
- upon whether an encode or decode is being done, which
- will have to be specified earlier. But it's worse: if
- input or output is from standard input or output, the
- mode will have to be changed on the fly, which is
- generally system and compiler dependent. 'Twasn't me
- who couldn't conform to Unix CR/LF convention, so
- don't ask me to write the code to work around
- Apple and Microsoft's incompatible standards. **/
-
- case 0:
- if (strcmp(cp, "-") != 0) {
- if ((fi = fopen(cp, "r")) == NULL) {
- fprintf(stderr, "Cannot open input file %s\n", cp);
- return 2;
- }
- }
- f++;
- break;
-
- case 1:
- if (strcmp(cp, "-") != 0) {
- if ((fo = fopen(cp, "w")) == NULL) {
- fprintf(stderr, "Cannot open output file %s\n", cp);
- return 2;
- }
- }
- f++;
- break;
-
- default:
- fprintf(stderr, "Too many file names specified.\n");
- usage(argv[0]);
- return 2;
- }
- }
- }
-
- if (decoding) {
- decode();
- } else {
- encode();
- }
- return 0;
-}
+++ /dev/null
-/*
- * This is simply a filter that converts Citadel binary message format
- * to readable, formatted output.
- *
- * If the -q (quiet or qwk) flag is used, only the message text prints, and
- * then it stops at the end of the first message it prints.
- *
- * This utility isn't very useful anymore.
- *
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <time.h>
-#include <errno.h>
-#include <libcitadel.h>
-
-int qwk = 0;
-
-int fpgetfield(FILE * fp, char *string);
-int fmout(int width, FILE * fp);
-
-
-
-int main(int argc, char **argv)
-{
- struct tm tm;
- int a, b, e, aflag;
- char bbb[1024];
- char subject[1024];
- FILE *fp;
- time_t now;
-
- if (argc == 2)
- if (!strcmp(argv[1], "-q"))
- qwk = 1;
- fp = stdin;
- if (argc == 2)
- if (strcmp(argv[1], "-q")) {
- fp = fopen(argv[1], "r");
- if (fp == NULL) {
- fprintf(stderr, "%s: cannot open %s: %s\n",
- argv[0], argv[1], strerror(errno));
- exit(errno);
- }
- }
-
-TOP: do {
- e = getc(fp);
- if (e < 0)
- exit(0);
- } while (e != 255);
- strcpy(subject, "");
- getc(fp);
- aflag = getc(fp);
- if (qwk == 0)
- printf(" ");
-
- do {
- b = getc(fp);
- if (b == 'M') {
- if (qwk == 0) {
- printf("\n");
- if (!IsEmptyStr(subject))
- printf("Subject: %s\n", subject);
- }
- if (aflag != 1)
- fmout(80, fp);
- else
- while (a = getc(fp), a > 0) {
- if (a == 13)
- putc(10, stdout);
- else
- putc(a, stdout);
- }
- }
- if ((b != 'M') && (b > 0))
- fpgetfield(fp, bbb);
- if (b == 'U')
- strcpy(subject, bbb);
- if (qwk == 0) {
- if (b == 'A')
- printf("from %s ", bbb);
- if (b == 'N')
- printf("@%s ", bbb);
- if (b == 'O')
- printf("in %s> ", bbb);
- if (b == 'R')
- printf("to %s ", bbb);
- if (b == 'T') {
- now = atol(bbb);
- localtime_r(&now, &tm);
- strcpy(bbb, asctime(&tm));
- bbb[strlen(bbb) - 1] = 0;
- printf("%s ", &bbb[4]);
- }
- }
- } while ((b != 'M') && (b > 0));
- if (qwk == 0)
- printf("\n");
- if (qwk == 1)
- exit(0);
- goto TOP;
-}
-
-int fpgetfield(FILE * fp, char *string)
-
-{ /* level-2 break out next null-terminated string */
- int a, b;
- strcpy(string, "");
- a = 0;
- do {
- b = getc(fp);
- if (b < 1) {
- string[a] = 0;
- return (0);
- }
- string[a] = b;
- ++a;
- } while (b != 0);
- return (0);
-}
-
-int fmout(int width, FILE * fp)
-{
- int a, b, c;
- int real = 0;
- int old = 0;
- char aaa[140];
-
- strcpy(aaa, "");
- old = 255;
- c = 1; /* c is the current pos */
-FMTA: old = real;
- a = getc(fp);
- real = a;
- if (a <= 0)
- goto FMTEND;
-
- if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
- a = 32;
- if (((old == 13) || (old == 10)) && (isspace(real))) {
- printf("\n");
- c = 1;
- }
- if (a > 126)
- goto FMTA;
-
- if (a > 32) {
- if (((strlen(aaa) + c) > (width - 5))
- && (strlen(aaa) > (width - 5))) {
- printf("\n%s", aaa);
- c = strlen(aaa);
- aaa[0] = 0;
- }
- b = strlen(aaa);
- aaa[b] = a;
- aaa[b + 1] = 0;
- }
- if (a == 32) {
- if ((strlen(aaa) + c) > (width - 5)) {
- printf("\n");
- c = 1;
- }
- printf("%s ", aaa);
- ++c;
- c = c + strlen(aaa);
- strcpy(aaa, "");
- goto FMTA;
- }
- if ((a == 13) || (a == 10)) {
- printf("%s\n", aaa);
- c = 1;
- strcpy(aaa, "");
- goto FMTA;
- }
- goto FMTA;
-
-FMTEND: printf("\n");
- return (0);
-}
+++ /dev/null
-
-/* This message is exactly 1024 bytes */
-char* const message =
-"The point of this little file is to stress test a Citadel server.\n"
-"It spawns n threads, where n is a command line parameter, each of\n"
-"which writes 1000 messages total to the server.\n"
-"\n"
-"-n is a command line parameter indicating how many users to simulate\n"
-"(default 100). WARNING: Your system must be capable of creating this\n"
-"many threads!\n"
-"\n"
-"-w is a command line parameter indicating how long to wait in seconds\n"
-"between posting each message (default 10). The actual interval\n"
-"will be randomized between w / 3 and w * 3.\n"
-"\n"
-"A run is expected to take approximately three hours, given default\n"
-"values, and assuming the server can keep up. If the run takes much\n"
-"longer than this, there may be a performance problem with the server.\n"
-"For best results, the test should be run from a different machine than\n"
-"the server, but connected via a fast network link (e.g. 100Base-T).\n"
-"\n"
-"To get baseline results, run the test with -n 1 (simulating 1 user)\n"
-"on a machine with no other users logged in.\n"
-"\n"
-"Example:\n"
-"stress -n 500 -w 25 myserver > stress.csv\n";
-
-/* The program tries to be as small and as fast as possible. Wherever
- * possible, we avoid allocating memory on the heap. We do not pass data
- * between threads. We do only a minimal amount of calculation. In
- * particular, we only output raw timing data for the run; we do not
- * collate it, average it, or do anything else with it. See below.
- * The program does, however, use the same CtdlIPC functions as the
- * standard Citadel text client, and incurs the same overhead as that
- * program, using those functions.
- *
- * The program first creates a new user with a randomized username which
- * begins with "testuser". It then creates 100 rooms named test0 through
- * test99. If they already exist, this condition is ignored.
- *
- * The program then creates n threads, all of which wait on a conditional
- * before they do anything. Once all of the threads have been created,
- * they are signaled, and begin execution. Each thread logs in to the
- * Citadel server separately, simulating a user login, then takes a
- * timestamp from the operating system.
- *
- * Each thread selects a room from 0-99 randomly, then writes a small
- * (1KB) test message to that room. 1K was chosen because it seems to
- * represent an average message size for messages we expect to see.
- * After writing the message, the thread sleeps for w seconds (sleep(w);)
- * and repeats the process, until it has written 1,000 messages. The
- * program provides a status display to standard error, unless w <= 2, in
- * which case status display is disabled.
- *
- * After posting all messages, each thread takes a second timestamp, and
- * subtracts the first timestamp. The resulting value (in seconds) is
- * sent to standard output, followed by the minimum, average, and maximum
- * amounts of time (in milliseconds) it took to post a message. The
- * thread then exits.
- *
- * Once all threads have exited, the program exits.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <string.h>
-#include <libcitadel.h>
-#include "sysdep.h"
-#include <time.h>
-#include "citadel_ipc.h"
-
-#ifndef HAVE_PTHREAD_H
-#error This program requires threads
-#endif
-
-static int w = 10; /* see above */
-static int n = 100; /* see above */
-static int m = 1000; /* Number of messages to send; see above */
-static volatile int count = 0; /* Total count of messages posted */
-static volatile int total = 0; /* Total messages to be posted */
-static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t arg_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t output_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static char username[12];
-static char password[12];
-
-/*
- * Mutex for the random number generator
- * We don't assume that rand_r() is present, so we have to
- * provide our own locking for rand()
- */
-static pthread_mutex_t rand_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/*
- * Conditional. All the threads wait for this signal to actually
- * start bombarding the server.
- */
-static pthread_mutex_t start_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t start_cond = PTHREAD_COND_INITIALIZER;
-
-
-/*
- * This is the worker thread. It logs in and creates the 1,000 messages
- * as described above.
- */
-void* worker(void* data)
-{
- CtdlIPC* ipc; /* My connection to the server */
- void** args; /* Args sent in */
- int r; /* IPC return code */
- char aaa[SIZ]; /* Generic buffer */
- int c; /* Message count */
- time_t start, end; /* Timestamps */
- struct ctdlipcmessage msg; /* The message we will post */
- int argc_;
- char** argv_;
- long tmin = LONG_MAX, trun = 0, tmax = LONG_MIN;
-
- args = (void*)data;
- argc_ = (int)args[0];
- argv_ = (char**)args[1];
-
- /* Setup the message we will be posting */
- msg.text = message;
- msg.anonymous = 0;
- msg.type = 1;
- strcpy(msg.recipient, "");
- strcpy(msg.subject, "Test message; ignore");
- strcpy(msg.author, username);
-
- pthread_mutex_lock(&arg_mutex);
- ipc = CtdlIPC_new(argc_, argv_, NULL, NULL);
- pthread_mutex_unlock(&arg_mutex);
- if (!ipc)
- return NULL; /* oops, something happened... */
-
- CtdlIPC_chat_recv(ipc, aaa);
- if (aaa[0] != '2') {
- fprintf(stderr, "Citadel refused me: %s\n", &aaa[4]);
- return NULL; /* server ran out of connections maybe? */
- }
-
- CtdlIPCIdentifySoftware(ipc, 8, 8, REV_LEVEL, "Citadel stress tester",
- "localhost", aaa); /* we're lying, the server knows */
-
- r = CtdlIPCQueryUsername(ipc, username, aaa);
- if (r / 100 == 2) {
- /* testuser already exists (from previous run?) */
- r = CtdlIPCTryLogin(ipc, username, aaa);
- if (r / 100 != 3) {
- fprintf(stderr, "Citadel refused username: %s\n", aaa);
- CtdlIPC_delete_ptr(&ipc);
- return NULL; /* Gawd only knows what went wrong */
- }
- r = CtdlIPCTryPassword(ipc, password, aaa);
- if (r / 100 != 2) {
- fprintf(stderr, "Citadel refused password: %s\n", aaa);
- CtdlIPC_delete_ptr(&ipc);
- return NULL; /* Gawd only knows what went wrong */
- }
- } else {
- /* testuser doesn't yet exist */
- r = CtdlIPCCreateUser(ipc, username, 1, aaa);
- if (r / 100 != 2) {
- fprintf(stderr, "Citadel refused create user: %s\n", aaa);
- CtdlIPC_delete_ptr(&ipc);
- return NULL; /* Gawd only knows what went wrong */
- }
- r = CtdlIPCChangePassword(ipc, password, aaa);
- if (r / 100 != 2) {
- fprintf(stderr, "Citadel refused change password: %s\n", aaa);
- CtdlIPC_delete_ptr(&ipc);
- return NULL; /* Gawd only knows what went wrong */
- }
- }
-
- /* Wait for the rest of the threads */
- pthread_mutex_lock(&start_mutex);
- pthread_cond_wait(&start_cond, &start_mutex);
- pthread_mutex_unlock(&start_mutex);
-
- /* And now the fun begins! Send out a whole shitload of messages */
- start = time(NULL);
- for (c = 0; c < m; c++) {
- int rm;
- char room[7];
- struct ctdlipcroom *rret;
- struct timeval tv;
- long tstart, tend;
- int wait;
-
- /* Wait for a while */
- pthread_mutex_lock(&rand_mutex);
- /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
- /* Randomize between w/3 to w*3 (yes, it's complicated) */
- wait = (int)((1.0+2.7*(float)w)*rand()/(RAND_MAX+(float)w/3.0)); /* range 0-99 */
- pthread_mutex_unlock(&rand_mutex);
- sleep(wait);
-
- /* Select the room to goto */
- pthread_mutex_lock(&rand_mutex);
- /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
- rm = (int)(100.0*rand()/(RAND_MAX+1.0)); /* range 0-99 */
- pthread_mutex_unlock(&rand_mutex);
-
- /* Goto the selected room */
- sprintf(room, "test%d", rm);
- /* Create the room if not existing. Ignore the return */
- r = CtdlIPCCreateRoom(ipc, 1, room, 0, NULL, 0, aaa);
- if (r / 100 != 2 && r != 574) { /* Already exists */
- fprintf(stderr, "Citadel refused room create: %s\n", aaa);
- pthread_mutex_lock(&count_mutex);
- total -= m - c;
- pthread_mutex_unlock(&count_mutex);
- CtdlIPC_delete_ptr(&ipc);
- return NULL;
- }
- gettimeofday(&tv, NULL);
- tstart = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* cvt to msec */
- r = CtdlIPCGotoRoom(ipc, room, "", &rret, aaa);
- if (r / 100 != 2) {
- fprintf(stderr, "Citadel refused room change: %s\n", aaa);
- pthread_mutex_lock(&count_mutex);
- total -= m - c;
- pthread_mutex_unlock(&count_mutex);
- CtdlIPC_delete_ptr(&ipc);
- return NULL;
- }
-
- /* Post the message */
- r = CtdlIPCPostMessage(ipc, 1, NULL, &msg, aaa);
- if (r / 100 != 4) {
- fprintf(stderr, "Citadel refused message entry: %s\n", aaa);
- pthread_mutex_lock(&count_mutex);
- total -= m - c;
- pthread_mutex_unlock(&count_mutex);
- CtdlIPC_delete_ptr(&ipc);
- return NULL;
- }
-
- /* Do a status update */
- pthread_mutex_lock(&count_mutex);
- count++;
- pthread_mutex_unlock(&count_mutex);
- fprintf(stderr, " %d/%d=%d%% \r",
- count, total,
- (int)(100 * count / total));
- gettimeofday(&tv, NULL);
- tend = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* cvt to msec */
- tend -= tstart;
- if (tend < tmin) tmin = tend;
- if (tend > tmax) tmax = tend;
- trun += tend;
- }
- end = time(NULL);
- pthread_mutex_lock(&output_mutex);
- fprintf(stderr, " \r");
- printf("%ld %ld %ld %ld\n", end - start, tmin, trun / c, tmax);
- pthread_mutex_unlock(&output_mutex);
- return (void*)(end - start);
-}
-
-
-/*
- * Shift argument list
- */
-int shift(int argc, char **argv, int start, int count)
-{
- int i;
-
- for (i = start; i < argc - count; ++i)
- argv[i] = argv[i + count];
- return argc - count;
-}
-
-
-/*
- * Main loop. Start a shitload of threads, all of which will attempt to
- * kick a Citadel server square in the nuts.
- */
-int main(int argc, char** argv)
-{
- void* data[2]; /* pass args to worker thread */
- pthread_t* threads; /* A shitload of threads */
- pthread_attr_t attr; /* Thread attributes (we use defaults) */
- int i; /* Counters */
- long runtime; /* Run time for each thread */
-
- /* Read argument list */
- for (i = 0; i < argc; i++) {
- if (!strcmp(argv[i], "-n")) {
- n = atoi(argv[i + 1]);
- argc = shift(argc, argv, i, 2);
- }
- if (!strcmp(argv[i], "-w")) {
- w = atoi(argv[i + 1]);
- argc = shift(argc, argv, i, 2);
- }
- if (!strcmp(argv[i], "-m")) {
- m = atoi(argv[i + 1]);
- argc = shift(argc, argv, i, 2);
- }
- if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
- fprintf(stderr, "Read stress.c for usage info\n");
- return 1;
- }
- }
-
- data[0] = (void*)argc; /* pass args to worker thread */
- data[1] = (void*)argv; /* pass args to worker thread */
-
- /* This is how many total messages will be posted */
- total = n * m;
-
- /* Pick a randomized username */
- pthread_mutex_lock(&rand_mutex);
- /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
- i = (int)(100.0*rand()/(RAND_MAX+1.0)); /* range 0-99 */
- pthread_mutex_unlock(&rand_mutex);
- sprintf(username, "testuser%d", i);
- strcpy(password, username);
-
- /* First, memory for our shitload of threads */
- threads = calloc(n, sizeof(pthread_t));
- if (!threads) {
- perror("Not enough memory");
- return 1;
- }
-
- /* Then thread attributes (all defaults for now) */
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- /* Then, create some threads */
- fprintf(stderr, "Creating threads \r");
- for (i = 0; i < n; ++i) {
- pthread_create(&threads[i], &attr, worker, (void*)data);
-
- /* Give thread #0 time to create the user account */
- if (i == 0) sleep(3);
- }
-
- //fprintf(stderr, "Starting in %d seconds\r", n);
- //sleep(n);
- fprintf(stderr, " \r");
-
- /* Then, signal the conditional they all are waiting on */
- pthread_mutex_lock(&start_mutex);
- pthread_cond_broadcast(&start_cond);
- pthread_mutex_unlock(&start_mutex);
-
- /* Then wait for them to exit */
- for (i = 0; i < n; i++) {
- pthread_join(threads[i], (void*)&runtime);
- /* We're ignoring this value for now... TODO */
- }
- fprintf(stderr, "\r \r");
- return 0;
-}