1 /* $Id: wip.c 8709 2009-11-06 21:47:11Z iulius $
2 **
3 ** Routines for keeping track of incoming articles, articles that haven't
4 ** acked from a duplex channel feed, and history caching.
5 **
6 ** WIP stands for work-in-progress
7 */
8 
9 #include "config.h"
10 #include "clibrary.h"
11 
12 #include "inn/innconf.h"
13 #include "innd.h"
14 
15 #define WIPTABLESIZE        1024
16 #define WIP_ARTMAX          300		/* innfeed default max send time */
17 
18 static WIP     *WIPtable[WIPTABLESIZE];      /* Top level of the WIP hash table */
19 
20 void
WIPsetup(void)21 WIPsetup(void)
22 {
23     memset(WIPtable, '\0', sizeof(WIPtable));
24 }
25 
26 /* Add a new entry into the table.  It is the appilications responsiblity to
27    to call WIPinprogress or WIPbyid first. */
28 WIP *
WIPnew(const char * messageid,CHANNEL * cp)29 WIPnew(const char *messageid, CHANNEL *cp)
30 {
31     HASH hash;
32     unsigned long bucket;
33     WIP *new;
34 
35     hash = Hash(messageid, strlen(messageid));
36     memcpy(&bucket, &hash,
37 	   sizeof(bucket) < sizeof(hash) ? sizeof(bucket) : sizeof(hash));
38     bucket = bucket % WIPTABLESIZE;
39 
40     new = xmalloc(sizeof(WIP));
41     new->MessageID = hash;
42     new->Timestamp = Now.tv_sec;
43     new->Chan = cp;
44     /* Link the new entry into the list */
45     new->Next = WIPtable[bucket];
46     WIPtable[bucket] = new;
47     return new;
48 }
49 
50 void
WIPprecomfree(CHANNEL * cp)51 WIPprecomfree(CHANNEL *cp)
52 {
53     WIP *cur;
54     int i;
55     if (cp == NULL)
56 	return;
57 
58     for (i = 0 ; i < PRECOMMITCACHESIZE ; i++) {
59 	cur = cp->PrecommitWIP[i];
60 	if (cur != (WIP *)NULL) {
61 	    WIPfree(cur);
62 	}
63     }
64 }
65 
66 void
WIPfree(WIP * wp)67 WIPfree(WIP *wp)
68 {
69     unsigned long bucket;
70     WIP *cur;
71     WIP *prev = NULL;
72     int i;
73     /* This is good error checking, but also allows us to
74           WIPfree(WIPbymessageid(id))
75        without having to check if id exists first */
76     if (wp == NULL)
77 	return;
78 
79     memcpy(&bucket, &wp->MessageID,
80 	   sizeof(bucket) < sizeof(HASH) ? sizeof(bucket) : sizeof(HASH));
81     bucket = bucket % WIPTABLESIZE;
82 
83     for (i = 0 ; i < PRECOMMITCACHESIZE ; i++) {
84 	if (wp->Chan->PrecommitWIP[i] == wp) {
85 	    wp->Chan->PrecommitWIP[i] = (WIP *)NULL;
86 	    break;
87 	}
88     }
89     for (cur = WIPtable[bucket]; (cur != NULL) && (cur != wp); prev = cur, cur = cur->Next);
90 
91     if (cur == NULL)
92 	return;
93 
94     if (prev == NULL)
95 	WIPtable[bucket] = cur->Next;
96     else
97 	prev->Next = cur->Next;
98 
99     /* unlink the entry and free the memory */
100     free(wp);
101 }
102 
103 /* Check if the given messageid is being transfered on another channel.  If
104    Add is true then add the given message-id to the current channel */
105 bool
WIPinprogress(const char * msgid,CHANNEL * cp,bool Precommit)106 WIPinprogress(const char *msgid, CHANNEL *cp, bool Precommit)
107 {
108     WIP *wp;
109     unsigned long i;
110 
111     if ((wp = WIPbyid(msgid)) != NULL) {
112 	if(wp->Chan->ArtBeg == 0)
113 	    i = 0;
114 	else {
115 	    i = wp->Chan->ArtMax;
116 	    if(i > WIP_ARTMAX)
117 		i = WIP_ARTMAX;
118 	}
119 
120 	if ((Now.tv_sec - wp->Timestamp) < (time_t) (i + innconf->wipcheck))
121 	    return true;
122 	if ((Now.tv_sec - wp->Timestamp) > (time_t) (i + innconf->wipexpire)) {
123 	    for (i = 0 ; i < PRECOMMITCACHESIZE ; i++) {
124 		if (wp->Chan->PrecommitWIP[i] == wp) {
125 		    wp->Chan->PrecommitWIP[i] = (WIP *)NULL;
126 		    break;
127 		}
128 	    }
129 	    WIPfree(wp);
130 	    WIPinprogress(msgid, cp, Precommit);
131 	    return false;
132 	}
133 	if (wp->Chan == cp)
134 	    return true;
135 	return false;
136     }
137     wp = WIPnew(msgid, cp);
138     if (Precommit) {
139 	if (cp->PrecommitiCachenext == PRECOMMITCACHESIZE)
140 	    cp->PrecommitiCachenext = 0;
141 	if (cp->PrecommitWIP[cp->PrecommitiCachenext])
142 	    WIPfree(cp->PrecommitWIP[cp->PrecommitiCachenext]);
143 	cp->PrecommitWIP[cp->PrecommitiCachenext++] = wp;
144     } else {
145 	WIPfree(WIPbyhash(cp->CurrentMessageIDHash));
146 	cp->CurrentMessageIDHash = wp->MessageID;
147     }
148     return false;
149 }
150 
151 WIP *
WIPbyid(const char * messageid)152 WIPbyid(const char *messageid)
153 {
154     HASH hash;
155     unsigned long bucket;
156     WIP *wp;
157 
158     hash = Hash(messageid, strlen(messageid));
159     memcpy(&bucket, &hash,
160 	   sizeof(bucket) < sizeof(hash) ? sizeof(bucket) : sizeof(hash));
161     bucket = bucket % WIPTABLESIZE;
162 
163     /* Traverse the list until we find a match or find the head again */
164     for (wp = WIPtable[bucket]; wp != NULL; wp = wp->Next)
165 	if (!memcmp(&hash, &wp->MessageID, sizeof(HASH)))
166 	    return wp;
167 
168     return NULL;
169 }
170 
171 WIP *
WIPbyhash(const HASH hash)172 WIPbyhash(const HASH hash)
173 {
174     unsigned long bucket;
175     WIP *wp;
176 
177     memcpy(&bucket, &hash,
178 	   sizeof(bucket) < sizeof(hash) ? sizeof(bucket) : sizeof(hash));
179     bucket = bucket % WIPTABLESIZE;
180 
181     /* Traverse the list until we find a match or find the head again */
182     for (wp = WIPtable[bucket]; wp != NULL; wp = wp->Next)
183 	if (!memcmp(&hash, &wp->MessageID, sizeof(HASH)))
184 	    return wp;
185 
186     return NULL;
187 }
188