*
* outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added
* whenever a new SNAC is sent and it should remain in the list until the
- * response for it has been receieved.
+ * response for it has been receieved.
*
- * First edition badly written by Adam Fritzler (afritz@delphid.ml.org)
- * Current edition nicely rewritten (it even works) by n (n@ml.org)
+ * cleansnacs() should be called periodically by the client in order
+ * to facilitate the aging out of unreplied-to SNACs. This can and does
+ * happen, so it should be handled.
*
*/
#include <faim/aim.h>
-u_long aim_newsnac(struct aim_session_t *sess,
- struct aim_snac_t *newsnac)
+/*
+ * Called from aim_session_init() to initialize the hash.
+ */
+faim_internal void aim_initsnachash(struct aim_session_t *sess)
+{
+ int i;
+
+ for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
+ sess->snac_hash[i] = NULL;
+ faim_mutex_init(&sess->snac_hash_locks[i]);
+ }
+
+ return;
+}
+
+faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess,
+ const unsigned short family,
+ const unsigned short type,
+ const unsigned short flags,
+ const void *data, const int datalen)
+{
+ struct aim_snac_t snac;
+
+ snac.id = sess->snac_nextid++;
+ snac.family = family;
+ snac.type = type;
+ snac.flags = flags;
+
+ snac.data = malloc(datalen);
+ memcpy(snac.data, data, datalen);
+
+ return aim_newsnac(sess, &snac);
+}
+
+/*
+ * Clones the passed snac structure and caches it in the
+ * list/hash.
+ */
+faim_internal unsigned long aim_newsnac(struct aim_session_t *sess,
+ struct aim_snac_t *newsnac)
{
- struct aim_snac_t *snac = NULL, *cur = NULL;
-
+ struct aim_snac_t *snac = NULL;
+ int index;
+
if (!newsnac)
return 0;
- cur = sess->outstanding_snacs;
-
- snac = calloc(1, sizeof(struct aim_snac_t));
- if (!snac)
+ if (!(snac = calloc(1, sizeof(struct aim_snac_t))))
return 0;
memcpy(snac, newsnac, sizeof(struct aim_snac_t));
snac->issuetime = time(&snac->issuetime);
snac->next = NULL;
-
- if (cur == NULL) {
- sess->outstanding_snacs = snac;
- return(snac->id);
- }
- while (cur->next != NULL)
- cur = cur->next;
- cur->next = snac;
+
+ index = snac->id % FAIM_SNAC_HASH_SIZE;
+
+ faim_mutex_lock(&sess->snac_hash_locks[index]);
+ snac->next = sess->snac_hash[index];
+ sess->snac_hash[index] = snac;
+ faim_mutex_unlock(&sess->snac_hash_locks[index]);
return(snac->id);
}
-struct aim_snac_t *aim_remsnac(struct aim_session_t *sess,
- u_long id)
+/*
+ * Finds a snac structure with the passed SNAC ID,
+ * removes it from the list/hash, and returns a pointer to it.
+ *
+ * The returned structure must be freed by the caller.
+ *
+ */
+faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *sess,
+ u_long id)
{
- struct aim_snac_t *cur;
+ struct aim_snac_t *cur = NULL;
+ int index;
- cur = sess->outstanding_snacs;
+ index = id % FAIM_SNAC_HASH_SIZE;
- if (cur == NULL)
- return(NULL);
-
- if (cur->id == id) {
- sess->outstanding_snacs = cur->next;
- return(cur);
- }
- while (cur->next != NULL) {
- if (cur->next->id == id) {
- struct aim_snac_t *tmp = NULL;
-
- tmp = cur->next;
- cur->next = cur->next->next;
- return(tmp);
+ faim_mutex_lock(&sess->snac_hash_locks[index]);
+ if (!sess->snac_hash[index])
+ ;
+ else if (sess->snac_hash[index]->id == id) {
+ cur = sess->snac_hash[index];
+ sess->snac_hash[index] = cur->next;
+ } else {
+ cur = sess->snac_hash[index];
+ while (cur->next) {
+ if (cur->next->id == id) {
+ struct aim_snac_t *tmp;
+
+ tmp = cur->next;
+ cur->next = cur->next->next;
+ cur = tmp;
+ break;
+ }
+ cur = cur->next;
}
- cur = cur->next;
}
- return(NULL);
+ faim_mutex_unlock(&sess->snac_hash_locks[index]);
+
+ return cur;
}
/*
* This is for cleaning up old SNACs that either don't get replies or
* a reply was never received for. Garabage collection. Plain and simple.
*
- * maxage is the _minimum_ age in seconds to keep SNACs (though I don't know
- * why its called _max_age).
+ * maxage is the _minimum_ age in seconds to keep SNACs.
*
*/
-int aim_cleansnacs(struct aim_session_t *sess,
- int maxage)
+faim_internal int aim_cleansnacs(struct aim_session_t *sess,
+ int maxage)
{
- struct aim_snac_t *cur;
- struct aim_snac_t *remed = NULL;
- time_t curtime;
-
- cur = sess->outstanding_snacs;
-
- curtime = time(&curtime);
-
- while (cur)
- {
- if ( (cur) && (((cur->issuetime) + maxage) < curtime))
- {
-#if DEBUG > 1
- printf("aimsnac: WARNING purged obsolete snac %08lx\n", cur->id);
-#endif
- remed = aim_remsnac(sess, cur->id);
- if (remed)
- {
- if (remed->data)
- free(remed->data);
- free(remed);
- }
- }
- cur = cur->next;
+ int i;
+
+ for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
+ struct aim_snac_t *cur = NULL, *next = NULL, *prev = NULL;
+ time_t curtime;
+
+ faim_mutex_lock(&sess->snac_hash_locks[i]);
+ if (!sess->snac_hash[i]) {
+ faim_mutex_unlock(&sess->snac_hash_locks[i]);
+ continue;
+ }
+
+ curtime = time(NULL); /* done here in case we waited for the lock */
+
+ cur = sess->snac_hash[i];
+ while (cur) {
+ next = cur->next;
+ if ((curtime - cur->issuetime) > maxage) {
+ if (sess->snac_hash[i] == cur)
+ prev = sess->snac_hash[i] = next;
+ else
+ prev->next = next;
+
+ /* XXX should we have destructors here? */
+ if (cur->data)
+ free(cur->data);
+ free(cur);
+
+ } else {
+ prev = cur;
+ }
+ cur = next;
}
-
+
+ faim_mutex_unlock(&sess->snac_hash_locks[i]);
+ }
+
return 0;
}
-int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid)
+faim_internal int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid)
{
int curbyte = 0;
curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff));