Parcourir la source

Merge branch 'userfile-stream'

* userfile-stream: (21 commits)
  * Update doc/UPDATES
  * Update bdlib and change stream.printf calls to use String.printf instead
  * Cleanup use of bd::Stream for bd::String passing, and move all userfile writing to 1 function.
  * Restore old base64 functions to ensure maximum compatibility.
  * EncryptedStream::loadFile causing double encryption
  * Fix two OPENSSL_cleanse() calls
  * Cleanup EncryptedString to only overload puts(String)
  * Update bdlib
  * Rename (encrypt|descrypt)_binary to aes_(encrypt|decrypt)_ecb_binary
  * Fix EncryptedStream to use external decoding functions
  * Use bd::String
  * Move encrypt_binary() and decrypt_binary() to aes_util.c
  * Add EncryptedStream using old broken_base64
  * Misc compile errors
  * ignore_time is an interval_t
  * Pass in the length when adding to the local stream
  * Use bd::Stream
  * Update bdlib to 3f3f30aca7923b96f5299172f394541ddef71261
  * Userfile transfer now takes place without an extra port (works from HUB behind NAT/firewall now)
  * Now using EncryptedStream to write/save userfiles
  ...
Bryan Drewery il y a 17 ans
Parent
commit
687dd94e65

+ 2 - 0
doc/UPDATES

@@ -1,3 +1,5 @@
+* Userfile transfer is now made over botlink (no extra ports needed) (works behind NAT/firewall)
+
 1.2.17 - http://wraith.botpack.net/milestone/1.2.17
 * Binary pass prompt has been changed to be more clear.
 * Fix uname checking on NetBSD and OpenBSD

+ 1 - 1
lib/bdlib

@@ -1 +1 @@
-Subproject commit 90f1cc9ffb8b84508d01928900fe10e2dd5c234d
+Subproject commit 6dbaaa8d5d2dcb03a7e6ad24785ab1a04ab576f1

+ 38 - 0
src/EncryptedStream.c

@@ -0,0 +1,38 @@
+/* EncryptedStream.c
+ *
+ */
+#include "base64.h"
+#include <bdlib/src/String.h>
+#include "EncryptedStream.h"
+#include <stdarg.h>
+#include "compat/compat.h"
+
+bd::String EncryptedStream::gets (size_t maxSize, char delim) {
+  if (!key.length()) return bd::Stream::gets(maxSize, delim);
+  bd::String tmp(bd::Stream::gets(maxSize, delim));
+  if (delim && tmp[tmp.length() - 1] == delim)
+    --tmp;
+  bd::String decrypted(decrypt_string(key, broken_base64Decode(tmp)));
+  /* The delimeter (\n) is not encrypted */
+  if (delim)
+    decrypted += delim;
+  return decrypted;
+}
+
+void EncryptedStream::puts (const bd::String& str_in)
+{
+  if (loading) {
+    bd::Stream::puts(str_in);
+    return;
+  }
+  bd::String string(str_in);
+  if (key.length()) {
+    if (string[string.length() - 1] == '\n')
+      --string;
+    bd::String encrypted(broken_base64Encode(encrypt_string(key, string)));
+    encrypted += '\n';
+    bd::Stream::puts(encrypted);
+    return;
+  }
+  bd::Stream::puts(string);
+}

+ 25 - 0
src/EncryptedStream.h

@@ -0,0 +1,25 @@
+#ifndef _ENCRYPTEDSTREAM_H
+#define _ENCRYPTEDSTREAM_H 1
+
+namespace bd {
+  class String;
+}
+#include <iostream>
+#include <bdlib/src/Stream.h>
+#include <bdlib/src/String.h>
+
+class EncryptedStream : public bd::Stream {
+  private:
+        bd::String key;
+
+  protected:
+
+  public:
+        EncryptedStream(const char* keyStr) : Stream(), key(bd::String(keyStr)) {};
+        EncryptedStream(bd::String& keyStr) : Stream(), key(keyStr) {};
+        EncryptedStream(EncryptedStream& stream) : Stream(stream), key(stream.key) {};
+
+        virtual bd::String gets(size_t, char delim = 0);
+        virtual void puts (const bd::String& string);
+};
+#endif

+ 1 - 0
src/Makefile.in

@@ -31,6 +31,7 @@ OBJS = auth.o \
 	debug.o \
 	egg_timer.o \
 	enclink.o \
+	EncryptedStream.o \
 	flags.o \
 	garble.o \
 	hash_table.o \

+ 93 - 4
src/base64.c

@@ -27,6 +27,13 @@
 #include <stdio.h>
 #include "base64.h"
 #include "src/compat/compat.h"
+#include <bdlib/src/String.h>
+
+static char *b64enc_bd(const unsigned char *data, size_t *len);
+static char *b64dec_bd(const unsigned char *data, size_t *len);
+static void b64enc_buf(const unsigned char *data, size_t len, char *dest);
+static void b64dec_buf(const unsigned char *data, size_t *len, char *dest);
+static void b64dec_bd_buf(const unsigned char *data, size_t *len, char *dest);
 
 static const char base64[65] = ".\\0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 static const char base64r[256] = {
@@ -101,6 +108,10 @@ char *int_to_base64(unsigned int val)
   return buf_base64 + i;
 }
 
+/* These are all broken */
+
+#define NUM_ASCII_BYTES 3
+#define NUM_ENCODED_BYTES 4
 
 char *
 b64enc(const unsigned char *data, size_t len)
@@ -111,13 +122,42 @@ b64enc(const unsigned char *data, size_t len)
   return (dest);
 }
 
-void
+/**
+ * @brief Base64 encode a string
+ * @param string The string to encode
+ * @return A new, encoded string
+ */
+bd::String broken_base64Encode(const bd::String& string) {
+  size_t len = string.length();
+  char *p = b64enc_bd((unsigned char*) string.data(), &len);
+  bd::String encoded(p, len);
+  free(p);
+  return encoded;
+}
+
+/**
+ * @brief Base64 decode a string
+ * @param string The string to decode
+ * @return A new, decoded string
+ */
+bd::String broken_base64Decode(const bd::String& string) {
+  size_t len = string.length();
+  char *p = b64dec_bd((unsigned char*) string.data(), &len);
+  bd::String decoded(p, len);
+  free(p);
+  return decoded;
+}
+
+
+/* Encode 3 8-bit bytes to 4 6-bit characters */
+static void
 b64enc_buf(const unsigned char *data, size_t len, char *dest)
 {
-#define DB(x) ((unsigned char) (x + i < len ? data[x + i] : 0))
+#define DB(x) ((unsigned char) ((x + i) < len ? data[x + i] : 0))
   register size_t t, i;
 
-  for (i = 0, t = 0; i < len; i += 3, t += 4) {
+  /* 4-byte blocks */
+  for (i = 0, t = 0; i < len; i += NUM_ASCII_BYTES, t += NUM_ENCODED_BYTES) {
     dest[t] = base64[DB(0) >> 2];
     dest[t + 1] = base64[((DB(0) & 3) << 4) | (DB(1) >> 4)];
     dest[t + 2] = base64[((DB(1) & 0x0F) << 2) | (DB(2) >> 6)];
@@ -127,6 +167,7 @@ b64enc_buf(const unsigned char *data, size_t len, char *dest)
   dest[t] = 0;
 }
 
+
 char *
 b64dec(const unsigned char *data, size_t *len)
 {
@@ -136,7 +177,7 @@ b64dec(const unsigned char *data, size_t *len)
   return (dest);
 }
 
-void
+static void
 b64dec_buf(const unsigned char *data, size_t *len, char *dest)
 {
 #define DB(x) ((unsigned char) (x + i < *len ? base64r[(unsigned char) data[x + i]] : 0))
@@ -153,3 +194,51 @@ b64dec_buf(const unsigned char *data, size_t *len, char *dest)
   dest[t] = 0;
   *len = t;
 }
+
+/* These are adapated for use with bd::String */
+
+static char *
+b64enc_bd(const unsigned char *data, size_t *len)
+{
+  size_t dlen = (((*len + (NUM_ASCII_BYTES - 1)) / NUM_ASCII_BYTES) * NUM_ENCODED_BYTES);
+  char *dest = (char *) my_calloc(1, dlen + 1);
+  b64enc_buf(data, *len, dest);
+  *len = dlen;
+  return (dest);
+}
+
+
+/* Decode 4 6-bit characters to 3 8-bit bytes */
+static void
+b64dec_bd_buf(const unsigned char *data, size_t *len, char *dest)
+{
+#define DB(x) ((unsigned char) (x + i < *len ? base64r[(unsigned char) data[x + i]] : 0))
+  register size_t t, i;
+  register int pads = 0;
+
+  for (i = 0, t = 0; i < *len; i += NUM_ENCODED_BYTES, t += NUM_ASCII_BYTES) {
+
+    dest[t] = (DB(0) << 2) + (DB(1) >> 4);
+    dest[t + 1] = ((DB(1) & 0x0F) << 4) + (DB(2) >> 2);
+    dest[t + 2] = ((DB(2) & 3) << 6) + DB(3);
+    /* Check for nulls (padding) - the >= check is because binary data might contain VALID NULLS */
+    if ((i + NUM_ENCODED_BYTES) >= *len) {
+      if (dest[t] == 0) ++pads;
+      if (dest[t+1] == 0) ++pads;
+      if (dest[t+2] == 0) ++pads;
+    }
+
+  };
+#undef DB
+
+  *len = t - pads;
+  dest[*len] = 0;
+}
+
+static char *
+b64dec_bd(const unsigned char *data, size_t *len)
+{
+  char *dest = (char *) my_calloc(1, ((*len * 3) >> 2) + 6 + 1);
+  b64dec_bd_buf(data, len, dest);
+  return dest;
+}

+ 5 - 2
src/base64.h

@@ -2,14 +2,17 @@
 #  define _BASE64_H_
 
 #include <sys/types.h>
+namespace bd {
+  class String;
+};
 
 char *int_to_base64(unsigned int);
 int base64_to_int(char *);
 
+bd::String broken_base64Encode(const bd::String&);
 char *b64enc(const unsigned char *data, size_t len);
-void b64enc_buf(const unsigned char *data, size_t len, char *dest);
 
+bd::String broken_base64Decode(const bd::String&);
 char *b64dec(const unsigned char *data, size_t *len);
-void b64dec_buf(const unsigned char *data, size_t *len, char *dest);
 
 #endif /* !_BASE64_H_ */

+ 2 - 2
src/binary.c

@@ -421,9 +421,9 @@ static void edpack(settings_t *incfg, const char *in_hash, int what)
   size_t len = 0;
 
   if (what == PACK_ENC)
-    enc_dec_string = encrypt_binary;
+    enc_dec_string = aes_encrypt_ecb_binary;
   else
-    enc_dec_string = decrypt_binary;
+    enc_dec_string = aes_decrypt_ecb_binary;
 
 #define dofield(_field) 		do {							\
 	if (_field) {										\

+ 34 - 72
src/crypt.c

@@ -13,76 +13,7 @@
 #include "base64.h"
 #include "src/crypto/crypto.h"
 #include <stdarg.h>
-
-#define CRYPT_BLOCKSIZE AES_BLOCK_SIZE
-#define CRYPT_KEYBITS 256
-#define CRYPT_KEYSIZE (CRYPT_KEYBITS >> 3)
-
-AES_KEY e_key, d_key;
-
-unsigned char *
-encrypt_binary(const char *keydata, unsigned char *in, size_t *inlen)
-{
-  size_t len = *inlen;
-  int blocks = 0, block = 0;
-  unsigned char *out = NULL;
-
-  /* First pad indata to CRYPT_BLOCKSIZE multiple */
-  if (len % CRYPT_BLOCKSIZE)             /* more than 1 block? */
-    len += (CRYPT_BLOCKSIZE - (len % CRYPT_BLOCKSIZE));
-
-  out = (unsigned char *) my_calloc(1, len + 1);
-  egg_memcpy(out, in, *inlen);
-  *inlen = len;
-
-  if (!keydata || !*keydata) {
-    /* No key, no encryption */
-    egg_memcpy(out, in, len);
-  } else {
-    char key[CRYPT_KEYSIZE + 1] = "";
-
-    strlcpy(key, keydata, sizeof(key));
-    AES_set_encrypt_key((const unsigned char *) key, CRYPT_KEYBITS, &e_key);
-    /* Now loop through the blocks and crypt them */
-    blocks = len / CRYPT_BLOCKSIZE;
-    for (block = blocks - 1; block >= 0; block--)
-      AES_encrypt(&out[block * CRYPT_BLOCKSIZE], &out[block * CRYPT_BLOCKSIZE], &e_key);
-    OPENSSL_cleanse(key, sizeof(key));
-    OPENSSL_cleanse(&e_key, sizeof(e_key));
-  }
-  out[len] = 0;
-  return out;
-}
-
-unsigned char *
-decrypt_binary(const char *keydata, unsigned char *in, size_t *len)
-{
-  int blocks = 0, block = 0;
-  unsigned char *out = NULL;
-
-  *len -= *len % CRYPT_BLOCKSIZE;
-  out = (unsigned char *) my_calloc(1, *len + 1);
-  egg_memcpy(out, in, *len);
-
-  if (!keydata || !*keydata) {
-    /* No key, no decryption */
-  } else {
-    /* Init/fetch key */
-    char key[CRYPT_KEYSIZE + 1] = "";
-
-    strlcpy(key, keydata, sizeof(key));
-    AES_set_decrypt_key((const unsigned char *) key, CRYPT_KEYBITS, &d_key);
-    /* Now loop through the blocks and crypt them */
-    blocks = *len / CRYPT_BLOCKSIZE;
-
-    for (block = blocks - 1; block >= 0; block--)
-      AES_decrypt(&out[block * CRYPT_BLOCKSIZE], &out[block * CRYPT_BLOCKSIZE], &d_key);
-    OPENSSL_cleanse(key, sizeof(key));
-    OPENSSL_cleanse(&d_key, sizeof(d_key));
-  }
-
-  return out;
-}
+#include <bdlib/src/String.h>
 
 char *encrypt_string(const char *keydata, char *in)
 {
@@ -91,7 +22,7 @@ char *encrypt_string(const char *keydata, char *in)
   char *res = NULL;
 
   len = strlen(in);
-  bdata = encrypt_binary(keydata, (unsigned char *) in, &len);
+  bdata = aes_encrypt_ecb_binary(keydata, (unsigned char *) in, &len);
   if (keydata && *keydata) {
     res = b64enc(bdata, len);
     OPENSSL_cleanse(bdata, len);
@@ -102,6 +33,21 @@ char *encrypt_string(const char *keydata, char *in)
   }
 }
 
+/**
+ * @brief Encrypt a string
+ * @param key The key to encrypt with
+ * @param data The string to encrypt
+ * @return A new, encrypted string
+ */
+bd::String encrypt_string(const bd::String& key, const bd::String& data) {
+  if (!key) return data;
+  size_t len = data.length();
+  char *bdata = (char*) aes_encrypt_ecb_binary(key.c_str(), (unsigned char*) data.c_str(), &len);
+  bd::String encrypted(bdata, len);
+  free(bdata);
+  return encrypted;
+}
+
 char *decrypt_string(const char *keydata, char *in)
 {
   size_t len = strlen(in);
@@ -109,7 +55,7 @@ char *decrypt_string(const char *keydata, char *in)
 
   if (keydata && *keydata) {
     buf = b64dec((const unsigned char *) in, &len);
-    res = (char *) decrypt_binary(keydata, (unsigned char *) buf, &len);
+    res = (char *) aes_decrypt_ecb_binary(keydata, (unsigned char *) buf, &len);
     OPENSSL_cleanse(buf, len);
     free(buf);
     return res;
@@ -120,6 +66,22 @@ char *decrypt_string(const char *keydata, char *in)
   }
 }
 
+/**
+ * @brief Decrypt a string
+ * @param key The key to decrypt with
+ * @param data The string to decrypt
+ * @return A new, decrypted string
+ */
+bd::String decrypt_string(const bd::String& key, const bd::String& data) {
+  if (!key) return data;
+  size_t len = data.length();
+  char *bdata = (char*) aes_decrypt_ecb_binary(key.c_str(), (unsigned char*) data.c_str(), &len);
+  bd::String decrypted(bdata, len);
+  OPENSSL_cleanse(bdata, len);
+  free(bdata);
+  return decrypted;
+}
+
 char *salted_sha1(const char *in, const char* saltin)
 {
   char *tmp = NULL, buf[101] = "", *ret = NULL;

+ 6 - 2
src/crypt.h

@@ -11,6 +11,10 @@
 #include "src/crypto/crypto.h"
 #include "users.h"
 
+namespace bd {
+  class String;
+};
+
 #define SHA_HASH_LENGTH (SHA_DIGEST_LENGTH << 1)
 #define MD5_HASH_LENGTH (MD5_DIGEST_LENGTH << 1)
 
@@ -23,11 +27,11 @@ char *MD5FILE(const char *);
 char *SHA1(const char *);
 int sha1cmp(const char *, const char*);
 
-unsigned char *encrypt_binary(const char *, unsigned char *, size_t *);
-unsigned char *decrypt_binary(const char *, unsigned char *, size_t *);
 char *encrypt_string(const char *, char *);
+bd::String encrypt_string(const bd::String&, const bd::String&);
 char *decrypt_string(const char *, char *);
 char *salted_sha1(const char *, const char* = NULL);
+bd::String decrypt_string(const bd::String&, const bd::String&);
 char *cryptit (char *);
 char *decryptit (char *);
 int lfprintf (FILE *, const char *, ...) __attribute__((format(printf, 2, 3)));

+ 1 - 0
src/crypto/Makefile.in

@@ -13,6 +13,7 @@ depcomp = /bin/sh $(top_srcdir)/autotools/depcomp
 @SET_MAKE@
 
 OBJS = aes.o \
+	aes_util.o \
 	cleanse.o \
 	md5.o \
 	sha.o

+ 78 - 0
src/crypto/aes_util.c

@@ -0,0 +1,78 @@
+/* aes_util.c
+ *
+ */
+
+#include "aes.h"
+#include "src/compat/compat.h"
+
+#define CRYPT_BLOCKSIZE AES_BLOCK_SIZE
+#define CRYPT_KEYBITS 256
+#define CRYPT_KEYSIZE (CRYPT_KEYBITS >> 3)
+
+AES_KEY e_key, d_key;
+
+unsigned char *
+aes_encrypt_ecb_binary(const char *keydata, unsigned char *in, size_t *inlen)
+{
+  size_t len = *inlen;
+  int blocks = 0, block = 0;
+  unsigned char *out = NULL;
+
+  /* First pad indata to CRYPT_BLOCKSIZE multiple */
+  if (len % CRYPT_BLOCKSIZE)             /* more than 1 block? */
+    len += (CRYPT_BLOCKSIZE - (len % CRYPT_BLOCKSIZE));
+
+  out = (unsigned char *) my_calloc(1, len + 1);
+  egg_memcpy(out, in, *inlen);
+  *inlen = len;
+
+  if (!keydata || !*keydata) {
+    /* No key, no encryption */
+    egg_memcpy(out, in, len);
+  } else {
+    char key[CRYPT_KEYSIZE + 1] = "";
+
+    strlcpy(key, keydata, sizeof(key));
+    AES_set_encrypt_key((const unsigned char *) key, CRYPT_KEYBITS, &e_key);
+    /* Now loop through the blocks and crypt them */
+    blocks = len / CRYPT_BLOCKSIZE;
+    for (block = blocks - 1; block >= 0; --block)
+      AES_encrypt(&out[block * CRYPT_BLOCKSIZE], &out[block * CRYPT_BLOCKSIZE], &e_key);
+    OPENSSL_cleanse(key, sizeof(key));
+    OPENSSL_cleanse(&e_key, sizeof(e_key));
+  }
+  out[len] = 0;
+  return out;
+}
+
+unsigned char *
+aes_decrypt_ecb_binary(const char *keydata, unsigned char *in, size_t *len)
+{
+  int blocks = 0, block = 0;
+  unsigned char *out = NULL;
+
+  *len -= *len % CRYPT_BLOCKSIZE;
+  out = (unsigned char *) my_calloc(1, *len + 1);
+  egg_memcpy(out, in, *len);
+
+  if (!keydata || !*keydata) {
+    /* No key, no decryption */
+  } else {
+    /* Init/fetch key */
+    char key[CRYPT_KEYSIZE + 1] = "";
+
+    strlcpy(key, keydata, sizeof(key));
+    AES_set_decrypt_key((const unsigned char *) key, CRYPT_KEYBITS, &d_key);
+    /* Now loop through the blocks and decrypt them */
+    blocks = *len / CRYPT_BLOCKSIZE;
+
+    for (block = blocks - 1; block >= 0; --block)
+      AES_decrypt(&out[block * CRYPT_BLOCKSIZE], &out[block * CRYPT_BLOCKSIZE], &d_key);
+    OPENSSL_cleanse(key, sizeof(key));
+    OPENSSL_cleanse(&d_key, sizeof(d_key));
+  }
+
+  *len = strlen((char*) out);
+  out[*len] = 0;
+  return out;
+}

+ 9 - 0
src/crypto/aes_util.h

@@ -0,0 +1,9 @@
+/* aes_util.h
+ *
+ */
+
+#ifndef _AES_UTIL_H
+#define _AES_UTIL_H 1
+unsigned char *aes_encrypt_ecb_binary(const char *, unsigned char *, size_t *);
+unsigned char *aes_decrypt_ecb_binary(const char *, unsigned char *, size_t *);
+#endif

+ 1 - 0
src/crypto/crypto.h

@@ -5,5 +5,6 @@
 #include "md5.h"
 #include "sha.h"
 #include "aes.h"
+#include "aes_util.h"
 
 #endif /* !_CRYPTO_H */

+ 10 - 6
src/mod/channels.mod/channels.h

@@ -24,6 +24,10 @@ static int count_mask(maskrec *);
 
 #endif				/* MAKING_CHANNELS */
 
+namespace bd {
+  class Stream;
+}
+
 void remove_channel(struct chanset_t *);
 void add_chanrec_by_handle(struct userrec *, char *, char *);
 void get_handle_chaninfo(char *, char *, char *);
@@ -31,11 +35,11 @@ void set_handle_chaninfo(struct userrec *, char *, char *, char *);
 struct chanuserrec *get_chanrec(struct userrec *u, char *);
 struct chanuserrec *add_chanrec(struct userrec *u, char *);
 void del_chanrec(struct userrec *, char *);
-bool write_bans(FILE *, int);
-bool write_exempts (FILE *, int);
-bool write_chans (FILE *, int);
-bool write_chans_compat (FILE *, int);
-bool write_invites (FILE *, int);
+void write_bans(bd::Stream&, int);
+void write_exempts(bd::Stream&, int);
+void write_chans(bd::Stream&, int);
+void write_chans_compat(bd::Stream&, int);
+void write_invites(bd::Stream&, int);
 bool expired_mask(struct chanset_t *, char *);
 void set_handle_laston(char *, struct userrec *, time_t);
 int u_delmask(char type, struct chanset_t *c, char *who, int doit);
@@ -51,7 +55,7 @@ bool u_match_mask(struct maskrec *, char *);
 bool ismasked(masklist *, const char *);
 bool ismodeline(masklist *, const char *);
 void channels_report(int, int);
-void channels_writeuserfile(bool = 0);
+void channels_writeuserfile(bd::Stream&, bool = 0);
 void rcmd_chans(char *, char *, char *);
 
 extern char		glob_chanset[512];

+ 68 - 104
src/mod/channels.mod/userchan.c

@@ -24,6 +24,8 @@
  */
 
 
+#include <bdlib/src/String.h>
+#include <bdlib/src/Stream.h>
 extern struct cmd_pass *cmdpass;
 
 struct chanuserrec *get_chanrec(struct userrec *u, char *chname)
@@ -568,168 +570,144 @@ static void tell_masks(const char type, int idx, bool show_inact, char *match, b
 
 /* Write the ban lists and the ignore list to a file.
  */
-bool write_bans(FILE *f, int idx)
+void write_bans(bd::Stream& stream, int idx)
 {
+  bd::String buf;
   if (global_ign)
-    if (lfprintf(f, IGNORE_NAME " - -\n") == EOF)	/* Daemus */
-      return 0;
+    stream << buf.printf(IGNORE_NAME " - -\n");
 
   char *mask = NULL;
 
   for (struct igrec *i = global_ign; i; i = i->next) {
     mask = str_escape(i->igmask, ':', '\\');
-    if (!mask ||
-	lfprintf(f, "- %s:%s%li:%s:%li:%s\n", mask,
+    if (mask) {
+	stream << buf.printf("- %s:%s%li:%s:%li:%s\n", mask,
 		(i->flags & IGREC_PERM) ? "+" : "", (long) i->expire,
 		i->user ? i->user : conf.bot->nick, (long) i->added,
-		i->msg ? i->msg : "") == EOF) {
-      if (mask)
-	free(mask);
-      return 0;
+		i->msg ? i->msg : "");
+        free(mask);
     }
-    free(mask);
   }
   if (global_bans)
-    if (lfprintf(f, BAN_NAME " - -\n") == EOF)	/* Daemus */
-      return 0;
+    stream << buf.printf(BAN_NAME " - -\n");
 
   maskrec *b = NULL;
 
   for (b = global_bans; b; b = b->next) {
     mask = str_escape(b->mask, ':', '\\');
-    if (!mask ||
-	lfprintf(f, "- %s:%s%li%s:+%li:%li:%s:%s\n", mask,
+    if (mask) {
+	stream << buf.printf("- %s:%s%li%s:+%li:%li:%s:%s\n", mask,
 		(b->flags & MASKREC_PERM) ? "+" : "", (long) b->expire,
 		(b->flags & MASKREC_STICKY) ? "*" : "", (long) b->added,
 		(long) b->lastactive, b->user ? b->user : conf.bot->nick,
-		b->desc ? b->desc : "requested") == EOF) {
-      if (mask)
-	free(mask);
-      return 0;
+		b->desc ? b->desc : "requested");
+      free(mask);
     }
-    free(mask);
   }
   for (struct chanset_t *chan = chanset; chan; chan = chan->next) {
-    if (lfprintf(f, "::%s bans\n", chan->dname) == EOF)
-      return 0;
+    stream << buf.printf("::%s bans\n", chan->dname);
+
     for (b = chan->bans; b; b = b->next) {
       mask = str_escape(b->mask, ':', '\\');
-      if (!mask ||
-        lfprintf(f, "- %s:%s%li%s:+%li:%li:%s:%s\n", mask,
+      if (mask) {
+        stream << buf.printf("- %s:%s%li%s:+%li:%li:%s:%s\n", mask,
 	        (b->flags & MASKREC_PERM) ? "+" : "", (long) b->expire,
 	        (b->flags & MASKREC_STICKY) ? "*" : "", (long) b->added,
 	        (long) b->lastactive, b->user ? b->user : conf.bot->nick,
-	        b->desc ? b->desc : "requested") == EOF) {
-          if (mask)
-            free(mask);
-          return 0;
-        }
-      free(mask);
+	        b->desc ? b->desc : "requested");
+        free(mask);
+      }
     }
   }
-  return 1;
 }
 /* Write the exemptlists to a file.
  */
-bool write_exempts(FILE *f, int idx)
+void write_exempts(bd::Stream& stream, int idx)
 {
+  bd::String buf;
+
   if (global_exempts)
-    if (lfprintf(f, EXEMPT_NAME " - -\n") == EOF) /* Daemus */
-      return 0;
+    stream << buf.printf(EXEMPT_NAME " - -\n");
 
   maskrec *e = NULL;
   char *mask = NULL;
 
   for (e = global_exempts; e; e = e->next) {
     mask = str_escape(e->mask, ':', '\\');
-    if (!mask ||
-        lfprintf(f, "%s %s:%s%li%s:+%li:%li:%s:%s\n", "%", mask,
+    if (mask) {
+        stream << buf.printf("%s %s:%s%li%s:+%li:%li:%s:%s\n", "%", mask,
 		(e->flags & MASKREC_PERM) ? "+" : "", (long) e->expire,
 		(e->flags & MASKREC_STICKY) ? "*" : "", (long) e->added,
 		(long) e->lastactive, e->user ? e->user : conf.bot->nick,
-		e->desc ? e->desc : "requested") == EOF) {
-      if (mask)
-	free(mask);
-      return 0;
+		e->desc ? e->desc : "requested");
+      free(mask);
     }
-    free(mask);
   }
   for (struct chanset_t *chan = chanset;chan ;chan = chan->next) {
-    if (lfprintf(f, "&&%s exempts\n", chan->dname) == EOF)
-      return 0;
+    stream << buf.printf("&&%s exempts\n", chan->dname);
     for (e = chan->exempts; e; e = e->next) {
       mask = str_escape(e->mask, ':', '\\');
-      if (!mask ||
-		lfprintf(f,"%s %s:%s%li%s:+%li:%li:%s:%s\n","%", mask,
+      if (mask) {
+	stream << buf.printf("%s %s:%s%li%s:+%li:%li:%s:%s\n","%", mask,
 		(e->flags & MASKREC_PERM) ? "+" : "", (long) e->expire,
 		(e->flags & MASKREC_STICKY) ? "*" : "", (long) e->added,
 		(long) e->lastactive, e->user ? e->user : conf.bot->nick,
-		e->desc ? e->desc : "requested") == EOF) {
-        if (mask)
-           free(mask);
-         return 0;
+		e->desc ? e->desc : "requested");
+        free(mask);
       }
-      free(mask);
     }
   }
-  return 1;
 }
 
 /* Write the invitelists to a file.
  */
-bool write_invites(FILE *f, int idx)
+void write_invites(bd::Stream& stream, int idx)
 {
+  bd::String buf;
 
   if (global_invites)
-    if (lfprintf(f, INVITE_NAME " - -\n") == EOF) /* Daemus */
-      return 0;
+    stream << buf.printf(INVITE_NAME " - -\n");
 
   maskrec *ir = NULL;
   char *mask = NULL;
 
   for (ir = global_invites; ir; ir = ir->next)  {
     mask = str_escape(ir->mask, ':', '\\');
-    if (!mask ||
-	lfprintf(f,"@ %s:%s%li%s:+%li:%li:%s:%s\n", mask,
+    if (mask) {
+      stream << buf.printf("@ %s:%s%li%s:+%li:%li:%s:%s\n", mask,
 		(ir->flags & MASKREC_PERM) ? "+" : "", (long) ir->expire,
 		(ir->flags & MASKREC_STICKY) ? "*" : "", (long) ir->added,
 		(long) ir->lastactive, ir->user ? ir->user : conf.bot->nick,
-		ir->desc ? ir->desc : "requested") == EOF) {
-      if (mask)
-	free(mask);
-      return 0;
+		ir->desc ? ir->desc : "requested");
+      free(mask);
     }
-    free(mask);
   }
   for (struct chanset_t *chan = chanset; chan; chan = chan->next) {
-    if (lfprintf(f, "$$%s invites\n", chan->dname) == EOF)
-      return 0;
+    stream << buf.printf("$$%s invites\n", chan->dname);
+
     for (ir = chan->invites; ir; ir = ir->next) {
       mask = str_escape(ir->mask, ':', '\\');
-      if (!mask ||
-	      lfprintf(f,"@ %s:%s%li%s:+%li:%li:%s:%s\n", mask,
+      if (mask) {
+        stream << buf.printf("@ %s:%s%li%s:+%li:%li:%s:%s\n", mask,
 		      (ir->flags & MASKREC_PERM) ? "+" : "", (long) ir->expire,
 		      (ir->flags & MASKREC_STICKY) ? "*" : "", (long) ir->added,
 		      (long) ir->lastactive, ir->user ? ir->user : conf.bot->nick,
-		      ir->desc ? ir->desc : "requested") == EOF) {
-        if (mask)
-	  free(mask);
-	return 0;
+		      ir->desc ? ir->desc : "requested");
+        free(mask);
       }
-      free(mask);
     }
   }
-  return 1;
 }
 
 /* Write the channels to the userfile
  */
-bool write_chans(FILE *f, int idx)
+void write_chans(bd::Stream& stream, int idx)
 {
+  bd::String buf;
+
   putlog(LOG_DEBUG, "*", "Writing channels..");
 
-  if (lfprintf(f, CHANS_NAME " - -\n") == EOF) /* Daemus */
-    return 0;
+  stream << buf.printf(CHANS_NAME " - -\n");
 
   char w[1024] = "";
 
@@ -746,7 +724,7 @@ bool write_chans(FILE *f, int idx)
     else
       inactive = PLSMNS(channel_inactive(chan));
 
-    if (lfprintf(f, "\
+    stream << buf.printf("\
 + channel add %s { chanmode { %s } addedby %s addedts %li \
 bad-cookie %d manop %d mdop %d mop %d limit %d \
 flood-chan %d:%d flood-ctcp %d:%d flood-join %d:%d \
@@ -819,21 +797,20 @@ flood-exempt %d flood-lock-time %d \
  * also include a %ctemp above.
  *      PLSMNS(channel_temp(chan)),
  */
-        ) == EOF)
-          return 0;
+    );
   }
-  return 1;
 }
 
 /* FIXME: remove after 1.2.14 */
 /* Write the channels to the userfile
  */
-bool write_chans_compat(FILE *f, int idx)
+void write_chans_compat(bd::Stream& stream, int idx)
 {
+  bd::String buf;
+
   putlog(LOG_DEBUG, "*", "Writing channels..");
 
-  if (lfprintf(f, CHANS_NAME " - -\n") == EOF) /* Daemus */
-    return 0;
+  stream << buf.printf(CHANS_NAME " - -\n");
 
   char w[1024] = "";
 
@@ -850,7 +827,7 @@ bool write_chans_compat(FILE *f, int idx)
     else
       inactive = PLSMNS(channel_inactive(chan));
 
-    if (lfprintf(f, "\
+    stream << buf.printf("\
 + channel add %s { chanmode { %s } addedby %s addedts %li \
 bad-cookie %d manop %d mdop %d mop %d limit %d \
 flood-chan %d:%d flood-ctcp %d:%d flood-join %d:%d \
@@ -918,34 +895,21 @@ exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
  * also include a %ctemp above.
  *      PLSMNS(channel_temp(chan)),
  */
-        ) == EOF)
-          return 0;
+    );
   }
-  return 1;
 }
 
-void channels_writeuserfile(bool old)
+void channels_writeuserfile(bd::Stream& stream, bool old)
 {
-  char s[1024] = "";
-  FILE *f = NULL;
-  int  ret = 0;
-
   putlog(LOG_DEBUG, "@", "Writing channel/ban/exempt/invite entries.");
-  simple_snprintf(s, sizeof(s), "%s~new", userfile);
-  f = fopen(s, "a");
-  if (f) {
-    if (!old)
-      ret  = write_chans(f, -1);
-    else
-      ret  = write_chans_compat(f, -1);
-    ret += write_vars_and_cmdpass(f, -1);
-    ret += write_bans(f, -1);
-    ret += write_exempts(f, -1);
-    ret += write_invites(f, -1);
-    fclose(f);
-  }
-  if (ret < 5)
-    putlog(LOG_MISC, "*", "ERROR writing user file.");
+  if (!old)
+    write_chans(stream, -1);
+  else
+    write_chans_compat(stream, -1);
+  write_vars_and_cmdpass(stream, -1);
+  write_bans(stream, -1);
+  write_exempts(stream, -1);
+  write_invites(stream, -1);
 }
 
 /* Expire mask originally set by `who' on `chan'?

+ 8 - 7
src/mod/console.mod/console.c

@@ -39,6 +39,8 @@
 #include "src/users.h"
 #include "src/misc.h"
 #include "src/core_binds.h"
+#include <bdlib/src/Stream.h>
+#include <bdlib/src/String.h>
 
 struct console_info {
   char *channel;
@@ -99,20 +101,19 @@ console_kill(struct user_entry *e)
   return 1;
 }
 
-static bool
-console_write_userfile(FILE * f, struct userrec *u, struct user_entry *e, int idx)
+static void
+console_write_userfile(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
   if (u->bot)
-    return 1;
+    return;
 
   struct console_info *i = (struct console_info *) e->u.extra;
+  bd::String buf;
 
-  if (lfprintf(f, "--CONSOLE %s %s %s %d %d %d %d %d %d %d %d\n",
+  stream << buf.printf("--CONSOLE %s %s %s %d %d %d %d %d %d %d %d\n",
                i->channel, masktype(i->conflags),
                stripmasktype(i->stripflags), i->echoflags,
-               i->page, i->conchan, i->color, i->banner, i->channels, i->bots, i->whom) == EOF)
-    return 0;
-  return 1;
+               i->page, i->conchan, i->color, i->banner, i->channels, i->bots, i->whom);
 }
 
 static bool

+ 92 - 45
src/mod/share.mod/share.c

@@ -37,6 +37,8 @@
 #include "src/botnet.h"
 #include "src/auth.h"
 #include "src/set.h"
+#include "src/EncryptedStream.h"
+#include <bdlib/src/String.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -54,6 +56,8 @@
 
 static struct flag_record fr = { 0, 0, 0, 0 };
 
+static bd::Stream stream_in;
+
 struct delay_mode {
   struct delay_mode *next;
   struct chanset_t *chan;
@@ -67,8 +71,13 @@ static struct delay_mode *start_delay = NULL;
 
 /* Prototypes */
 static void start_sending_users(int);
-static void shareout_but(int, const char *, ...)  __attribute__ ((format(printf, 2, 3)));
-
+static void stream_send_users(int);
+static void share_read_stream(int, bd::Stream&);
+#ifdef __GNUC__
+ static void shareout_but(int, const char *, ...)  __attribute__ ((format(printf, 2, 3)));
+#else
+ static void shareout_but(int, const char *, ...);
+#endif
 static bool cancel_user_xfer_staylinked = 0;
 static void cancel_user_xfer(int, void *);
 
@@ -900,7 +909,20 @@ share_ufyes(int idx, char *par)
 
     lower_bot_linked(idx);
 
-    start_sending_users(idx);
+    if (strstr(par, "stream")) {
+      updatebot(-1, dcc[idx].nick, '+', 0, 0, 0, NULL);
+      /* Start up a tbuf to queue outgoing changes for this bot until the
+       * userlist is done transferring.
+       */
+      new_tbuf(dcc[idx].nick);
+      /* override shit removed here */
+      q_tbuf(dcc[idx].nick, "s !\n");
+      dcc[idx].status |= STAT_SENDING;
+      stream_send_users(idx);
+      dump_resync(idx);
+      dcc[idx].status &= ~STAT_SENDING;
+    } else
+      start_sending_users(idx);
     putlog(LOG_BOTS, "@", "Sending user file send request to %s", dcc[idx].nick);
   }
 }
@@ -928,7 +950,7 @@ share_userfileq(int idx, char *par)
       dprintf(idx, "s un Already sharing.\n");
     else {
       dcc[idx].u.bot->uff_flags |= (UFF_OVERRIDE | UFF_INVITE | UFF_EXEMPT);
-      dprintf(idx, "s uy overbots invites exempts\n");
+      dprintf(idx, "s uy overbots invites exempts stream\n");
       /* Set stat-getting to astatic void race condition (robey 23jun1996) */
       dcc[idx].status |= STAT_SHARE | STAT_GETTING | STAT_AGGRESSIVE;
       if (conf.bot->hub)
@@ -1052,6 +1074,23 @@ share_end(int idx, char *par)
   dcc[idx].u.bot->uff_flags = 0;
 }
 
+static void share_userfile_line(int idx, char *par) {
+  char *size = newsplit(&par);
+
+  stream_in << bd::String(par, atoi(size));
+  stream_in << '\n';
+}
+
+static void share_userfile_start(int idx, char *par) {
+  dcc[idx].status |= STAT_GETTING;
+  stream_in.clear();
+}
+
+static void share_userfile_end(int idx, char *par) {
+  stream_in.seek(0, SEEK_SET);
+  share_read_stream(idx, stream_in);
+}
+
 /* Note: these MUST be sorted. */
 static botcmd_t C_share[] = {
   {"!", share_endstartup, 0},
@@ -1073,6 +1112,9 @@ static botcmd_t C_share[] = {
   {"e", share_end, 0},
   {"h", share_chhand, 0},
   {"k", share_killuser, 0},
+  {"l", share_userfile_line, 0},
+  {"le", share_userfile_end, 0},
+  {"ls", share_userfile_start, 0},
   {"ms", share_stick_mask, 0},
   {"n", share_newuser, 0},
   {"u?", share_userfileq, 0},
@@ -1206,36 +1248,13 @@ static bool
 write_tmp_userfile(char *fn, const struct userrec *bu, int idx)
 {
   FILE *f = NULL;
-  int ok = 0;
+  int ok = 1;
 
   if ((f = fopen(fn, "wb"))) {
-    fchmod(fileno(f), S_IRUSR | S_IWUSR);
-/* FIXME: REMOVE AFTER 1.2.14 */
-    bool old = 0;
-
-    tand_t* bot = idx != -1 ? findbot(dcc[idx].nick) : NULL;
-    if (bot && bot->buildts < 1175102242) /* flood-* hacks */
-      old = 1;
-
-    time_t tt = now;
-
-    lfprintf(f, "#4v: %s -- %s -- written %s", ver, conf.bot->nick, ctime(&tt));
-
-    if (!old)
-      ok += write_chans(f, idx);
-    else
-      ok += write_chans_compat(f, idx);
-    ok += write_vars_and_cmdpass(f, idx);
-    ok += write_bans(f, idx);
-    ok += write_exempts(f, idx);
-    ok += write_invites(f, idx);
-    if (ok != 5)
+    if (real_writeuserfile(idx, bu, f))
       ok = 0;
-    for (struct userrec *u = (struct userrec *) bu; u && ok; u = u->next) {
-      if (!write_user(u, f, idx))
-        ok = 0;
-    }
     fclose(f);
+    fixmod(fn);
   }
   if (!ok)
     putlog(LOG_MISC, "*", "ERROR writing user file to transfer.");
@@ -1247,8 +1266,6 @@ write_tmp_userfile(char *fn, const struct userrec *bu, int idx)
 void
 finish_share(int idx)
 {
-  struct userrec *u = NULL, *ou = NULL;
-  struct chanset_t *chan = NULL;
   int i, j = -1;
 
   for (i = 0; i < dcc_total; i++)
@@ -1259,6 +1276,12 @@ finish_share(int idx)
   if (j == -1)
     return;
 
+  const char salt1[] = SALT1;
+  EncryptedStream stream(salt1);
+  stream.loadFile(dcc[idx].u.xfer->filename);
+  unlink(dcc[idx].u.xfer->filename);
+  share_read_stream(j, stream);
+
 /* compress.mod 
   if (!uncompressfile(dcc[idx].u.xfer->filename)) {
     char xx[1024] = "";
@@ -1277,6 +1300,11 @@ finish_share(int idx)
     return;
   }
 */
+}
+static void share_read_stream(int idx, bd::Stream& stream) {
+  struct userrec *u = NULL, *ou = NULL;
+  struct chanset_t *chan = NULL;
+  int i;
 
   /*
    * This is where we remove all global and channel bans/exempts/invites and
@@ -1325,9 +1353,7 @@ finish_share(int idx)
   if (conf.bot->u)
     conf.bot->u = NULL;
 
-  struct cmd_pass *old_cmdpass = NULL;
-
-  old_cmdpass = cmdpass;
+  struct cmd_pass *old_cmdpass = cmdpass;
   cmdpass = NULL;
 
   /* Read the transferred userfile. Add entries to u, which already holds
@@ -1336,12 +1362,11 @@ finish_share(int idx)
   loading = 1;
   checkchans(0);                /* flag all the channels.. */
   Context;
-  if (!readuserfile(dcc[idx].u.xfer->filename, &u)) {   /* read the userfile into 'u' */
+  if (!stream_readuserfile(stream, &u)) {   /* read the userfile into 'u' */
     /* FAILURE */
     char xx[1024] = "";
 
     Context;
-    unlink(dcc[idx].u.xfer->filename);
     clear_userlist(u);          /* Clear new, obsolete, user list.      */
     clear_chanlist();           /* Remove all user references from the
                                  * channel lists.                       */
@@ -1366,20 +1391,18 @@ finish_share(int idx)
     /* old userlist is now being used, safe to do this stuff... */
     loading = 0;
     putlog(LOG_MISC, "*", "%s", "CAN'T READ NEW USERFILE");
-    dprintf(idx, "bye\n");
-    simple_snprintf(xx, sizeof xx, "Disconnected %s (can't read userfile)", dcc[j].nick);
-    botnet_send_unlinked(j, dcc[j].nick, xx);
+//    dprintf(idx, "bye\n");
+    simple_snprintf(xx, sizeof xx, "Disconnected %s (can't read userfile)", dcc[idx].nick);
+    botnet_send_unlinked(idx, dcc[idx].nick, xx);
     chatout("*** %s\n", xx);
 
-    killsock(dcc[j].sock);
-    lostdcc(j);
+    killsock(dcc[idx].sock);
+    lostdcc(idx);
     return;
   }
 
   /* SUCCESS! */
 
-  unlink(dcc[idx].u.xfer->filename);
-
   loading = 0;
   clear_chanlist();             /* Remove all user references from the
                                  * channel lists.                       */
@@ -1414,7 +1437,7 @@ finish_share(int idx)
   checkchans(1);                /* remove marked channels */
   var_parse_my_botset();
   reaffirm_owners();            /* Make sure my owners are +a   */
-  updatebot(-1, dcc[j].nick, '+', 0, 0, 0, NULL);
+  updatebot(-1, dcc[idx].nick, '+', 0, 0, 0, NULL);
   send_sysinfo();
 
   /* Prevents the server connect from dumping JOIN #chan */
@@ -1432,6 +1455,30 @@ finish_share(int idx)
 
 /* Begin the user transfer process.
  */
+static void
+ulsend(int idx, const char* data, size_t datalen)
+{
+  char buf[1040] = "";
+
+  size_t len = simple_snprintf(buf, sizeof(buf), "s l %d %s", datalen-1, data);/* -1 for newline */
+  tputs(dcc[idx].sock, buf, len);
+}
+
+static void
+stream_send_users(int idx)
+{
+  bd::Stream stream;
+  stream_writeuserfile(stream, userlist, idx);
+  stream.seek(0, SEEK_SET);
+  dprintf(idx, "s ls\n");
+  bd::String buf;
+  while (stream.tell() < stream.length()) {
+    buf = stream.getline(1024);
+    ulsend(idx, buf.c_str(), buf.length());
+  }
+  dprintf(idx, "s le\n");
+}
+
 static void
 start_sending_users(int idx)
 {

+ 8 - 9
src/set.c

@@ -20,6 +20,8 @@
 #include "userrec.h"
 #include "userent.h"
 #include "rfc1459.h"
+#include <bdlib/src/Stream.h>
+#include <bdlib/src/String.h>
 
 #include "set_default.h"
 
@@ -823,25 +825,22 @@ static char *var_rem_list(const char *botnick, variable_t *var, const char *elem
   return ret;
 }
 
-bool write_vars_and_cmdpass(FILE *f, int idx)
+void write_vars_and_cmdpass(bd::Stream& stream, int idx)
 {
+  bd::String buf;
+
   putlog(LOG_DEBUG, "@", "Writing set entries...");
-  if (lfprintf(f, SET_NAME " - -\n") == EOF) /* Daemus */
-      return 0;
+  stream << buf.printf(SET_NAME " - -\n");
 
   int i = 0;
 
   for (i = 0; vars[i].name; i++) {
     /* send blanks if our variable isn't set, theirs MIGHT be set and needs to be UNSET */
-    if (lfprintf(f, "@ %s %s\n", vars[i].name, vars[i].gdata ? vars[i].gdata : "") == EOF)
-      return 0;
+    stream << buf.printf("@ %s %s\n", vars[i].name, vars[i].gdata ? vars[i].gdata : "");
   }
 
   for (struct cmd_pass *cp = cmdpass; cp; cp = cp->next)
-    if (lfprintf(f, "- %s %s\n", cp->name, cp->pass) == EOF)
-      return 0;
-
-  return 1;
+    stream << buf.printf("- %s %s\n", cp->name, cp->pass);
 }
 
 

+ 5 - 1
src/set.h

@@ -76,7 +76,11 @@ extern int		cloak_script, fight_threshold, fork_interval, in_bots, set_noshare,
                         ison_time;
 extern rate_t		op_requests, close_threshold;
 
-bool write_vars_and_cmdpass (FILE *, int);
+namespace bd {
+  class Stream;
+}
+
+void write_vars_and_cmdpass (bd::Stream&, int);
 void var_userfile_share_line(char *, int, bool);
 void var_parse_my_botset();
 void init_vars();

+ 20 - 25
src/userent.c

@@ -37,6 +37,8 @@
 #include "dccutil.h"
 #include "crypt.h"
 #include "botmsg.h"
+#include <bdlib/src/Stream.h>
+#include <bdlib/src/String.h>
 
 static struct user_entry_type *entry_type_list = NULL;
 
@@ -91,21 +93,19 @@ bool def_kill(struct user_entry *e)
   return 1;
 }
 
-bool write_userfile_protected(FILE * f, struct userrec *u, struct user_entry *e, int idx)
+void write_userfile_protected(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
   /* only write if saving local, or if sending to hub, or if sending to same user as entry */
   if (idx == -1 || dcc[idx].hub || dcc[idx].user == u) {
-    if (lfprintf(f, "--%s %s\n", e->type->name, e->u.string) == EOF)
-      return 0;
+    bd::String buf;
+    stream << buf.printf("--%s %s\n", e->type->name, e->u.string);
   }
-  return 1;
 }
 
-bool def_write_userfile(FILE * f, struct userrec *u, struct user_entry *e, int idx)
+void def_write_userfile(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
-  if (lfprintf(f, "--%s %s\n", e->type->name, e->u.string) == EOF)
-    return 0;
-  return 1;
+  bd::String buf;
+  stream << buf.printf("--%s %s\n", e->type->name, e->u.string);
 }
 
 void *def_get(struct userrec *u, struct user_entry *e)
@@ -362,17 +362,16 @@ static bool set_gotshare(struct userrec *u, struct user_entry *e, char *buf, int
   return 1;
 }
 
-static bool set_write_userfile(FILE *f, struct userrec *u, struct user_entry *e, int idx)
+static void set_write_userfile(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
   /* only write if saving local, or if sending to hub, or if sending to same user as entry */
   if (idx == -1 || dcc[idx].hub || dcc[idx].user == u) {
     struct xtra_key *x = (struct xtra_key *) e->u.extra;
+    bd::String buf;
 
     for (; x; x = x->next)
-      if (lfprintf(f, "--%s %s %s\n", e->type->name, x->key, x->data ? x->data : "") == EOF)
-        return 0;
+      stream << buf.printf("--%s %s %s\n", e->type->name, x->key, x->data ? x->data : "");
   }
-  return 1;
 }
 
 static bool set_kill(struct user_entry *e)
@@ -700,13 +699,12 @@ static bool laston_unpack(struct userrec *u, struct user_entry *e)
   return 1;
 }
 
-static bool laston_write_userfile(FILE * f, struct userrec *u, struct user_entry *e, int idx)
+static void laston_write_userfile(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
   struct laston_info *li = (struct laston_info *) e->u.extra;
+  bd::String buf;
 
-  if (lfprintf(f, "--LASTON %li %s\n", (long) li->laston, li->lastonplace ? li->lastonplace : "") == EOF)
-    return 0;
-  return 1;
+  stream << buf.printf("--LASTON %li %s\n", (long) li->laston, li->lastonplace ? li->lastonplace : "");
 }
 
 static bool laston_kill(struct user_entry *e)
@@ -824,14 +822,12 @@ static bool botaddr_kill(struct user_entry *e)
   return 1;
 }
 
-static bool botaddr_write_userfile(FILE *f, struct userrec *u, struct user_entry *e, int idx)
+static void botaddr_write_userfile(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
   register struct bot_addr *bi = (struct bot_addr *) e->u.extra;
+  bd::String buf;
 
-  if (lfprintf(f,  "--%s %s:%u/%u:%u:%s\n", e->type->name, bi->address,
-  	      bi->telnet_port, bi->relay_port, bi->hublevel, bi->uplink) == EOF)
-    return 0;
-  return 1;
+  stream << buf.printf("--%s %s:%u/%u:%u:%s\n", e->type->name, bi->address, bi->telnet_port, bi->relay_port, bi->hublevel, bi->uplink);
 }
 
 static bool botaddr_set(struct userrec *u, struct user_entry *e, void *buf)
@@ -914,14 +910,13 @@ struct user_entry_type USERENTRY_BOTADDR =
   "BOTADDR"
 };
 
-static bool hosts_write_userfile(FILE *f, struct userrec *u, struct user_entry *e, int idx)
+static void hosts_write_userfile(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
   struct list_type *h = NULL;
+  bd::String buf;
 
   for (h = (struct list_type *) e->u.extra; h; h = h->next)
-    if (lfprintf(f, "--HOSTS %s\n", h->extra) == EOF)
-      return 0;
-  return 1;
+    stream << buf.printf("--HOSTS %s\n", h->extra);
 }
 
 static bool hosts_null(struct userrec *u, struct user_entry *e)

+ 42 - 32
src/userrec.c

@@ -49,6 +49,8 @@
 #include "core_binds.h"
 #include "socket.h"
 #include "net.h"
+#include "EncryptedStream.h"
+#include <bdlib/src/String.h>
 
 bool             noshare = 1;		/* don't send out to sharebots	    */
 struct userrec	*userlist = NULL;	/* user records are stored here	    */
@@ -354,14 +356,14 @@ int u_pass_match(struct userrec *u, char *in)
   return 0;
 }
 
-bool write_user(struct userrec *u, FILE * f, int idx)
+static void write_user(const struct userrec *u, bd::Stream& stream, int idx)
 {
   char s[181] = "";
   struct flag_record fr = {FR_GLOBAL, u->flags, 0, 0 };
+  bd::String buf;
 
   build_flags(s, &fr, NULL);
-  if (lfprintf(f, "%s%-10s - %-24s\n", u->bot ? "-" : "", u->handle, s) == EOF)
-    return 0;
+  stream << buf.printf("%s%-10s - %-24s\n", u->bot ? "-" : "", u->handle, s);
 
   struct chanset_t *cst = NULL;
 
@@ -375,8 +377,7 @@ bool write_user(struct userrec *u, FILE * f, int idx)
       fr.match = FR_CHAN;
       fr.chan = ch->flags;
       build_flags(s, &fr, NULL);
-      if (lfprintf(f, "! %-20s %li %-10s %s\n", ch->channel, (long) ch->laston, s, ch->info ? ch->info : "") == EOF)
-        return 0;
+      stream << buf.printf("! %-20s %li %-10s %s\n", ch->channel, (long) ch->laston, s, ch->info ? ch->info : "");
     }
   }
   for (struct user_entry *ue = u->entries; ue; ue = ue->next) {
@@ -390,10 +391,9 @@ bool write_user(struct userrec *u, FILE * f, int idx)
     } else
 #endif
     if (ue->type)
-      if (conf.bot->hub && !ue->type->write_userfile(f, u, ue, idx))
-	return 0;
+      if (conf.bot->hub)
+        ue->type->write_userfile(stream, u, ue, idx);
   }
-  return 1;
 }
 
 static int sort_compare(struct userrec *a, struct userrec *b)
@@ -463,6 +463,20 @@ static void sort_userlist()
   }
 }
 
+void stream_writeuserfile(bd::Stream& stream, const struct userrec *bu, int idx, bool old) {
+  time_t tt = now;
+  char s1[81] = "";
+  bd::String buf;
+
+  strcpy(s1, ctime(&tt));
+
+  stream << buf.printf("#4v: %s -- %s -- written %s", ver, conf.bot->nick, s1);
+  channels_writeuserfile(stream, old);
+
+  for (const struct userrec *u = bu; u; u = u->next)
+    write_user(u, stream, -1);
+}
+
 /* Rewrite the entire user file. Call USERFILE hook as well, probably
  * causing the channel file to be rewritten as well.
  */
@@ -487,38 +501,17 @@ int write_userfile(int idx)
   fchmod(fileno(f), S_IRUSR | S_IWUSR);
 
   char backup[DIRMAX] = "";
-  bool ok = 1;
 
   if (idx >= 0)
     dprintf(idx, "Saving userfile...\n");
+
   if (sort_users)
     sort_userlist();
 
-  time_t tt = now;
-
-  lfprintf(f, "#4v: %s -- %s -- written %s", ver, conf.bot->nick, ctime(&tt));
-  fclose(f);
-
-
-/* FIXME: REMOVE AFTER 1.2.14 */
-  bool old = 0;
-
-  tand_t* bot = idx != -1 ? findbot(dcc[idx].nick) : NULL;
-  if (bot && bot->buildts < 1175102242) /* flood-* hacks */
-    old = 1;
-  channels_writeuserfile(old);
-
-  f = fopen(new_userfile, "a");
-  if (f == NULL) {
-    putlog(LOG_MISC, "*", "ERROR writing user file.");
-    free(new_userfile);
-    return 2;
-  }
 
   putlog(LOG_DEBUG, "@", "Writing user entries.");
-  for (struct userrec *u = userlist; u && ok; u = u->next)
-    ok = write_user(u, f, -1);
-  if (!ok || fflush(f)) {
+
+  if (real_writeuserfile(idx, userlist, f)) {
     putlog(LOG_MISC, "*", "ERROR writing user file. (%s)", strerror(ferror(f)));
     fclose(f);
     free(new_userfile);
@@ -533,6 +526,23 @@ int write_userfile(int idx)
   return 0;
 }
 
+/* Used by writeuserfile() and write_tmp_userfile() in share.c */
+int real_writeuserfile(int idx, const struct userrec *bu, FILE *f) {
+/* FIXME: REMOVE AFTER 1.2.14 */
+  bool old = 0;
+
+  tand_t* bot = idx != -1 ? findbot(dcc[idx].nick) : NULL;
+  if (bot && bot->buildts < 1175102242) /* flood-* hacks */
+    old = 1;
+
+  const char salt1[] = SALT1;
+  EncryptedStream stream(salt1);
+  stream_writeuserfile(stream, bu, idx, old);
+  if ((fwrite(bd::String(stream).data(), 1, stream.length(), f) != stream.length()) || (fflush(f)))
+    return 1;
+  return 0;
+}
+
 int change_handle(struct userrec *u, char *newh)
 {
   if (!u)

+ 6 - 1
src/userrec.h

@@ -1,6 +1,10 @@
 #ifndef _USERREC_H
 #define _USERREC_H
 
+namespace bd {
+  class Stream;
+}
+
 struct userrec *adduser(struct userrec *, char *, char *, char *, flag_t, int);
 void addhost_by_handle(char *, char *);
 void clear_masks(struct maskrec *);
@@ -11,8 +15,9 @@ int count_users(struct userrec *);
 int deluser(char *);
 int change_handle(struct userrec *, char *);
 void correct_handle(char *);
-bool write_user(struct userrec *u, FILE * f, int shr);
+void stream_writeuserfile(bd::Stream&, const struct userrec *, int, bool = 0);
 int write_userfile(int);
+int real_writeuserfile(int idx, const struct userrec *bu, FILE *f);
 void touch_laston(struct userrec *, char *, time_t);
 void user_del_chan(char *);
 struct userrec *host_conflicts(char *);

+ 19 - 24
src/users.c

@@ -54,6 +54,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include "misc_file.h"
+#include "EncryptedStream.h"
 
 char userfile[121] = "";	/* where the user records are stored */
 interval_t ignore_time = 10;		/* how many minutes will ignores last? */
@@ -585,16 +586,22 @@ void backup_userfile()
  */
 
 int readuserfile(const char *file, struct userrec **ret)
+{
+  const char salt1[] = SALT1;
+  EncryptedStream stream(salt1);
+  stream.loadFile(file);
+  return stream_readuserfile(stream, ret);
+}
+
+int stream_readuserfile(bd::Stream& stream, struct userrec **ret)
 {
   char *p = NULL, buf[1024] = "", lasthand[512] = "", *attr = NULL, *pass = NULL;
-  char *code = NULL, s1[1024] = "", *s = buf, cbuf[1024] = "", *temps = NULL, ignored[512] = "";
-  FILE *f = NULL;
+  char *code = NULL, s1[1024] = "", *s = buf, ignored[512] = "";
   struct userrec *bu = NULL, *u = NULL;
   struct chanset_t *cst = NULL;
   struct flag_record fr;
   struct chanuserrec *cr = NULL;
   int i, line = 0;
-  const char salt1[] = SALT1;
 
   bu = (*ret);
   if (bu == userlist) {
@@ -605,33 +612,23 @@ int readuserfile(const char *file, struct userrec **ret)
     global_exempts = NULL;
     global_invites = NULL;
   }
-  f = fopen(file, "r");
-  if (f == NULL)
-    return 0;
   noshare = 1;
   /* read opening comment */
-  fgets(cbuf, 180, f);
-  remove_crlf(cbuf);
-  temps = (char *) decrypt_string(salt1, cbuf);
-  simple_snprintf(s, 180, "%s", temps);
-  free(temps);
-  if (s[1] < '4') {
+  bd::String str(stream.getline(180));
+  if (str[1] < '4') {
     putlog(LOG_MISC, "*", "!*! Empty or malformed userfile.");
     return 0;
   }
-  if (s[1] > '4') {
+  if (str[1] > '4') {
     putlog(LOG_MISC, "*", "Invalid userfile format.");
     return 0;
   }
-  while (!feof(f)) {
+  while (stream.tell() < stream.length()) {
     s = buf;
-    fgets(cbuf, 1024, f);
-    remove_crlf(cbuf);
-    temps = (char *) decrypt_string(salt1, cbuf);
-    simple_snprintf(s, 1024, "%s", temps);
-    OPENSSL_cleanse(temps, strlen(temps));
-    free(temps);
-    if (!feof(f)) {
+    str = stream.getline(sizeof(buf));
+    strlcpy(s, str.c_str(), std::min(str.length(), sizeof(buf)));
+    remove_crlf(s);
+    if (1) {
       line++;
       if (s[0] != '#' && s[0] != ';' && s[0]) {
 	code = newsplit(&s);
@@ -739,7 +736,6 @@ int readuserfile(const char *file, struct userrec **ret)
            if (channel_add(resultbuf, chan, options) != OK) {
              putlog(LOG_MISC, "*", "Channel parsing error in userfile on line %d", line);
              free(my_ptr);
-             fclose(f);
              noshare = 0;
              return 0;
            }
@@ -862,7 +858,6 @@ int readuserfile(const char *file, struct userrec **ret)
 	  if (!attr[0] || !pass[0]) {
 	    putlog(LOG_MISC, "*", "* Corrupt user record line: %d!", line);
 	    lasthand[0] = 0;
-            fclose(f);
             noshare = 0;
             return 0;
 	  } else {
@@ -910,7 +905,7 @@ int readuserfile(const char *file, struct userrec **ret)
       }
     }
   }
-  fclose(f);
+
   (*ret) = bu;
   if (ignored[0]) {
     putlog(LOG_MISC, "*", "Ignored masks for channel(s): %s", ignored);

+ 5 - 1
src/users.h

@@ -25,6 +25,9 @@ bool list_append(struct list_type **, struct list_type *);
 bool list_delete(struct list_type **, struct list_type *);
 bool list_contains(struct list_type *, struct list_type *);
 
+namespace bd {
+  class Stream;
+}
 
 /* New userfile format stuff
  */
@@ -34,7 +37,7 @@ struct user_entry_type {
   struct user_entry_type *next;
   bool (*got_share) (struct userrec *, struct user_entry *, char *, int);
   bool (*unpack) (struct userrec *, struct user_entry *);
-  bool (*write_userfile) (FILE *, struct userrec *, struct user_entry *, int);
+  void (*write_userfile) (bd::Stream&, const struct userrec *, const struct user_entry *, int);
   bool (*kill) (struct user_entry *);
   void *(*get) (struct userrec *, struct user_entry *);
   bool (*set) (struct userrec *, struct user_entry *, void *);
@@ -171,6 +174,7 @@ void tell_file_stats(int, char *);
 void tell_user_ident(int, char *);
 void tell_users_match(int, char *, int, int, char *, int);
 int readuserfile(const char *, struct userrec **);
+int stream_readuserfile(bd::Stream&, struct userrec **);
 void check_pmode();
 void link_pref_val(struct userrec *u, char *lval);
 void check_stale_dcc_users();