Fri, 27 Apr 2001 22:21:53 +0000
[gaim-migrate @ 1773]
la la la
| 1535 | 1 | /* |
| 2 | * | |
| 3 | * Various SNAC-related dodads... | |
| 4 | * | |
| 5 | * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added | |
| 6 | * whenever a new SNAC is sent and it should remain in the list until the | |
| 7 | * response for it has been receieved. | |
| 8 | * | |
| 9 | * cleansnacs() should be called periodically by the client in order | |
| 10 | * to facilitate the aging out of unreplied-to SNACs. This can and does | |
| 11 | * happen, so it should be handled. | |
| 12 | * | |
| 13 | */ | |
| 14 | ||
| 15 | #define FAIM_INTERNAL | |
| 16 | #include <aim.h> | |
| 17 | ||
| 18 | /* | |
| 19 | * Called from aim_session_init() to initialize the hash. | |
| 20 | */ | |
| 21 | faim_internal void aim_initsnachash(struct aim_session_t *sess) | |
| 22 | { | |
| 23 | int i; | |
| 24 | ||
| 25 | for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { | |
| 26 | sess->snac_hash[i] = NULL; | |
| 27 | faim_mutex_init(&sess->snac_hash_locks[i]); | |
| 28 | } | |
| 29 | ||
| 30 | return; | |
| 31 | } | |
| 32 | ||
| 33 | faim_internal unsigned long aim_cachesnac(struct aim_session_t *sess, | |
| 34 | const unsigned short family, | |
| 35 | const unsigned short type, | |
| 36 | const unsigned short flags, | |
| 37 | const void *data, const int datalen) | |
| 38 | { | |
| 39 | struct aim_snac_t snac; | |
| 40 | ||
| 41 | snac.id = sess->snac_nextid++; | |
| 42 | snac.family = family; | |
| 43 | snac.type = type; | |
| 44 | snac.flags = flags; | |
| 45 | ||
| 46 | if (datalen) { | |
| 47 | if (!(snac.data = malloc(datalen))) | |
| 48 | return 0; /* er... */ | |
| 49 | memcpy(snac.data, data, datalen); | |
| 50 | } else | |
| 51 | snac.data = NULL; | |
| 52 | ||
| 53 | return aim_newsnac(sess, &snac); | |
| 54 | } | |
| 55 | ||
| 56 | /* | |
| 57 | * Clones the passed snac structure and caches it in the | |
| 58 | * list/hash. | |
| 59 | */ | |
| 60 | faim_internal unsigned long aim_newsnac(struct aim_session_t *sess, | |
| 61 | struct aim_snac_t *newsnac) | |
| 62 | { | |
| 63 | struct aim_snac_t *snac = NULL; | |
| 64 | int index; | |
| 65 | ||
| 66 | if (!newsnac) | |
| 67 | return 0; | |
| 68 | ||
| 69 | if (!(snac = calloc(1, sizeof(struct aim_snac_t)))) | |
| 70 | return 0; | |
| 71 | memcpy(snac, newsnac, sizeof(struct aim_snac_t)); | |
| 72 | snac->issuetime = time(&snac->issuetime); | |
| 73 | snac->next = NULL; | |
| 74 | ||
| 75 | index = snac->id % FAIM_SNAC_HASH_SIZE; | |
| 76 | ||
| 77 | faim_mutex_lock(&sess->snac_hash_locks[index]); | |
| 78 | snac->next = sess->snac_hash[index]; | |
| 79 | sess->snac_hash[index] = snac; | |
| 80 | faim_mutex_unlock(&sess->snac_hash_locks[index]); | |
| 81 | ||
| 82 | return(snac->id); | |
| 83 | } | |
| 84 | ||
| 85 | /* | |
| 86 | * Finds a snac structure with the passed SNAC ID, | |
| 87 | * removes it from the list/hash, and returns a pointer to it. | |
| 88 | * | |
| 89 | * The returned structure must be freed by the caller. | |
| 90 | * | |
| 91 | */ | |
| 92 | faim_internal struct aim_snac_t *aim_remsnac(struct aim_session_t *sess, | |
| 93 | u_long id) | |
| 94 | { | |
| 95 | struct aim_snac_t *cur = NULL; | |
| 96 | int index; | |
| 97 | ||
| 98 | index = id % FAIM_SNAC_HASH_SIZE; | |
| 99 | ||
| 100 | faim_mutex_lock(&sess->snac_hash_locks[index]); | |
| 101 | if (!sess->snac_hash[index]) | |
| 102 | ; | |
| 103 | else if (sess->snac_hash[index]->id == id) { | |
| 104 | cur = sess->snac_hash[index]; | |
| 105 | sess->snac_hash[index] = cur->next; | |
| 106 | } else { | |
| 107 | cur = sess->snac_hash[index]; | |
| 108 | while (cur->next) { | |
| 109 | if (cur->next->id == id) { | |
| 110 | struct aim_snac_t *tmp; | |
| 111 | ||
| 112 | tmp = cur->next; | |
| 113 | cur->next = cur->next->next; | |
| 114 | cur = tmp; | |
| 115 | break; | |
| 116 | } | |
| 117 | cur = cur->next; | |
| 118 | } | |
| 119 | } | |
| 120 | faim_mutex_unlock(&sess->snac_hash_locks[index]); | |
| 121 | ||
| 122 | return cur; | |
| 123 | } | |
| 124 | ||
| 125 | /* | |
| 126 | * This is for cleaning up old SNACs that either don't get replies or | |
| 127 | * a reply was never received for. Garabage collection. Plain and simple. | |
| 128 | * | |
| 129 | * maxage is the _minimum_ age in seconds to keep SNACs. | |
| 130 | * | |
| 131 | */ | |
| 132 | faim_internal int aim_cleansnacs(struct aim_session_t *sess, | |
| 133 | int maxage) | |
| 134 | { | |
| 135 | int i; | |
| 136 | ||
| 137 | for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) { | |
| 138 | struct aim_snac_t *cur, **prev; | |
| 139 | time_t curtime; | |
| 140 | ||
| 141 | faim_mutex_lock(&sess->snac_hash_locks[i]); | |
| 142 | if (!sess->snac_hash[i]) { | |
| 143 | faim_mutex_unlock(&sess->snac_hash_locks[i]); | |
| 144 | continue; | |
| 145 | } | |
| 146 | ||
| 147 | curtime = time(NULL); /* done here in case we waited for the lock */ | |
| 148 | ||
| 149 | for (prev = &sess->snac_hash[i]; (cur = *prev); ) { | |
| 150 | if ((curtime - cur->issuetime) > maxage) { | |
| 151 | ||
| 152 | *prev = cur->next; | |
| 153 | ||
| 154 | /* XXX should we have destructors here? */ | |
| 155 | if (cur->data) | |
| 156 | free(cur->data); | |
| 157 | free(cur); | |
| 158 | ||
| 159 | } else | |
| 160 | prev = &cur->next; | |
| 161 | } | |
| 162 | ||
| 163 | faim_mutex_unlock(&sess->snac_hash_locks[i]); | |
| 164 | } | |
| 165 | ||
| 166 | return 0; | |
| 167 | } | |
| 168 | ||
| 169 | faim_internal int aim_putsnac(u_char *buf, int family, int subtype, int flags, u_long snacid) | |
| 170 | { | |
| 171 | int curbyte = 0; | |
| 172 | curbyte += aimutil_put16(buf+curbyte, (u_short)(family&0xffff)); | |
| 173 | curbyte += aimutil_put16(buf+curbyte, (u_short)(subtype&0xffff)); | |
| 174 | curbyte += aimutil_put16(buf+curbyte, (u_short)(flags&0xffff)); | |
| 175 | curbyte += aimutil_put32(buf+curbyte, snacid); | |
| 176 | return curbyte; | |
| 177 | } |