1-- startup.lua: version 1.1.99/07
2--
3-- NMDC Listeners:
4--   chat		= normal chat message (you won't get your own messages)
5--   			  f( hub, user, "message" )
6--   			  DISCARDABLE:
7--   			    return non-nil to hide the msg from BCDC++,
8--   			    all discardable functions also get an optional last
9--   			    argument stating whether some previous listener returned non-nil
10--   unknownchat= incoming messages displayed without any nick
11--   			  f( hub, "message" )
12--   			  DISCARDABLE
13--   ownChat	= normal chat message from yourself (incoming from the hub)
14--   			  f( hub, "message" )
15--   			  DISCARDABLE
16--   pm			= normal pm message
17--   			  f( hub, user, "message" )
18--   			  DISCARDABLE
19--   hubPm		= pm message with a different prefix than the nick in the From field
20--   			  f( hub, user, "message which may include a <nickname>" )
21--   			  DISCARDABLE
22--   userConnected = a user connected (or rather.. the first getUser call was made)
23--   			  f( hub, user )
24--   userMyInfo	= a myinfo message from a user, use this as "userConnected"
25--   			  f( hub, user, "$MyINFO $ALL ... 1234$|" )
26--   			  DISCARDABLE
27--   userQuit	= a quit message from a user
28--   			  f( hub, nick )
29--   			  DISCARDABLE
30--   raw		= a full message
31--   			  f( hub, "line" )
32--   			  DISCARDABLE (message won't get parsed.. no other listeners will be called)
33-- ADC Listeners:
34--   adcChat	= normal chat message (you won't get your own messages).
35--   			  f( hub, user, "message", me_msg )
36--   			  DISCARDABLE:
37--   			    * return non-nil to hide the msg from BCDC++,
38--   			    * all discardable functions also get an optional last
39--   			      argument stating whether some previous listener returned non-nil
40--					* me_msg is true if a /me-style message is sent
41--   adcOwnChat	= normal chat message from yourself (incoming from the hub)
42--   			  f( hub, "message", me_msg )
43--   			  DISCARDABLE
44--   adcPm		= normal pm message
45--   			  f( hub, user, "message", me_msg )
46--   			  DISCARDABLE
47--   groupPm	= pm message with a different reply-sid than the one who talks (probably chatroom or bot)
48--   			  f( hub, user, "message", reply_sid, me_msg )
49--   			  DISCARDABLE
50--   userInf	= an INF message from a user
51--   			  f( hub, user, flags )
52--					flags is a table where all named flags are stored (ie: flags["NI"], flags["I4"], ...)
53--   			  DISCARDABLE
54--   adcUserCon = a user connected (or rather.. the first getUser call was made)
55--   			  f( hub, user )
56--   adcUserQui	= a quit message from a user
57--   			  f( hub, sid, flags )
58--					flags is a table where all named flags are stored (ie: flags["ID"], flags["MS"], ...)
59-- Common Listeners:
60--   ownChatOut	= normal chat message from yourself (outgoing to the hub)
61--   			  f( hub, "message" )
62--   			  DISCARDABLE
63--   connected	= connecting to a hub (many functions may be unavailable (return nil))
64--   			  f( hub )
65--   disconnected = disconnected from a hub
66--   			  f( hub )
67--   timer		= called every second (if DC():RunTimer(1) is called), use
68--   			  dcpp:getHubs()[k]:getAddress() or :getUrl() to select a hub
69--   			  f()
70--   clientIn	= peer to peer line oriented transfer control input, userp is a pointer
71--   			  (lightuserdata)
72--   			  f( userp, "line" )
73--   			  DISCARDABLE (returning non-nil kills the connection)
74--   clientOut	= peer to peer line oriented transfer control output, userp is a pointer
75--   			  (lightuserdata)
76--   			  f( userp, "line" )
77--   			  DISCARDABLE (returning non-nil kills the connection)
78--
79-- Example listener (load _after_ this script):
80--		dcpp:setListener( "chat", "bier",
81--			function( hub, user, text )
82--				local s = string.lower( text )
83--				if string.find( s, "[^a-z]bier+[^a-z]" ) or string.find( s, "[^a-z]biertje[^a-z]" ) then
84--					hub:sendChat( "bier? ja lekker! :)" )
85--				end
86--			end
87--		)
88--
89-- If you want to remove the "bier" chat listener, simply type:
90-- /lua dcpp:setListener( "chat", "bier" )
91-- in any chat window
92
93
94DC():PrintDebug( "** Started startup.lua **" )
95
96--/////////////////////////////////////
97--// Helper functions
98--/////////////////////////////////////
99
100if not dcu then
101	dcu = {}
102end
103
104dcu.NmdcEscape = function(this, msg )
105	msg = string.gsub( msg, "%$", "&#36;")
106	msg = string.gsub( msg, "|", "&#124;")
107	return msg
108end
109
110dcu.AdcEscape = function(this, msg, inverse)
111	msg = string.gsub(msg, "\r", "")
112	local ret = ""
113	if inverse then
114		local replacetable = {}
115		replacetable["\\\\"] = [[\]]
116		replacetable["\\s"] = [[ ]]
117		replacetable["\\n"] = "\n"
118		local skip = false
119		for k = 1, string.len(msg) do
120			if skip then
121				skip = false
122			else
123				local c = string.sub( msg, k, k + 1)
124				if replacetable[c] then
125					ret = ret .. replacetable[c]
126					skip = true
127				else
128					ret = ret .. string.sub(c, 1, 1)
129				end
130			end
131		end
132	else
133		local replacetable = {}
134		replacetable["\\"] = [[\\]]
135		replacetable[" "] = [[\s]]
136		replacetable["\n"] = [[\n]]
137
138		for k = 1, string.len(msg) do
139			local c = string.sub( msg, k, k)
140			if replacetable[c] then
141				ret = ret .. replacetable[c]
142			else
143				ret = ret .. c
144			end
145		end
146	end
147	return ret
148end
149
150--// Checks if the given decimal number has the 2^exp bit set
151dcu.BBit = function(this, num, exp)
152	local ret = false
153	if string.find(tostring(math.floor(num / 2^exp)), "[13579]$") then
154		ret = true
155	end
156	return ret
157end
158
159--/////////////////////////////////////
160--// Hub manager
161--/////////////////////////////////////
162
163if not dcpp or dcpp._init_me_anyway == true then
164	dcpp = {}
165	dcpp._hubs = {}
166	dcpp._listeners = {}
167end
168
169dcpp.addHub = function( this, hub, isitadc )
170	if not this._hubs[hub] then
171		-- DC():PrintDebug( "Hub added (id = "..tostring( hub )..")" )
172		if isitadc then
173			this._hubs[hub] = createAdcHub( hub )
174			DC():PrintDebug( "ADC hub added (id = " .. tostring( hub ) ..")" )
175		else
176			this._hubs[hub] = createNmdcHub( hub )
177		end
178		for k,f in pairs(dcpp:getListeners( "connected" )) do
179			f( this._hubs[hub] )
180		end
181		return this._hubs[hub]
182	else
183		--DC():PrintDebug( "Tried to add existing hub on: "..this._hubs[hub]:getHubName()..
184		--						" (id = "..tostring( hub )..")" )
185		return nil
186	end
187end
188
189dcpp.getHub = function( this, hub )
190	if this._hubs[hub] then
191		return this._hubs[hub]
192	else
193		--DC():PrintDebug( "Tried to fetch an unknown hub (id = "..tostring( hub )..")" )
194		return this:addHub( hub ):setPartial() -- tell the object that we missed the logon
195	end
196end
197
198dcpp.getHubs = function( this )
199	return this._hubs
200end
201
202dcpp.hasHub = function( this, hub )
203	if this._hubs[hub] then
204		return 1
205	else
206		return nil
207	end
208end
209
210dcpp.findHub = function( this, url )
211	for k, h in pairs(this._hubs) do
212		if h:getUrl() == url then
213			return h
214		end
215	end
216	return false
217end
218
219dcpp.removeHub = function( this, hub )
220	if this._hubs[hub] then
221		--DC():PrintDebug( "Hub removed: "..this._hubs[hub]:getHubName().." (id = "..tostring( hub )..")" )
222		for k,f in pairs(dcpp:getListeners( "disconnected" )) do
223			f( this._hubs[hub] )
224		end
225		this._hubs[hub]:destroy()
226		this._hubs[hub] = nil
227	else
228		--DC():PrintDebug( "Tried to remove non-existent hub (id = "..tostring( hub )..")" )
229	end
230end
231
232dcpp.listHubs = function( this )
233	DC():PrintDebug( "** Listing hubs **" )
234	for k,v in pairs(this._hubs) do
235		DC():PrintDebug( tostring( k ).." ("..v:getHubName()..")" )
236	end
237end
238
239dcpp.setListener = function( this, ltype, id, func )
240	if not dcpp._listeners[ltype] then
241		dcpp._listeners[ltype] = {}
242	end
243	dcpp._listeners[ltype][id] = func
244end
245
246dcpp.getListeners = function( this, ltype )
247	if dcpp._listeners[ltype] then
248		return dcpp._listeners[ltype]
249	else
250		return {}
251	end
252end
253
254
255--/////////////////////////////////////
256--// Hub object
257--/////////////////////////////////////
258
259function createAdcHub( hubid )
260	local hub = {}
261
262	hub._id = hubid
263	hub._mySID = nil
264	hub._myNick = nil
265	hub._myCID = nil
266	hub._nick = nil
267	hub._description = nil
268	hub._users = {}
269	hub._uptime = os.time()
270
271	hub.getId = function( this )
272		return this._id
273	end
274
275	hub.getUptime = function( this )
276		return ( os.time() - hub._uptime ) / 60
277	end
278
279	hub.getProtocol = function( this )
280		return "adc"
281	end
282
283	hub.getAddress = function( this )
284		return DC():GetHubIpPort( this._id )
285	end
286
287	hub.getHubName = function( this )
288		--// TODO: Shall we return with NI or DE? or both?
289		--//       For now just returning the nick with the url
290		if this._nick then
291			return this._nick  .. " ("..this:getUrl()..")"
292		else
293			return this:getUrl()
294		end
295	end
296
297	hub.getUrl = function( this )
298		return DC():GetHubUrl( this._id )
299	end
300
301	hub.getOwnNick = function( this )
302		return this._myNick
303	end
304
305	hub.setOwnNick = function( this, newnick )
306		this._myNick = newnick
307		DC():PrintDebug("[STATUS] Own nick set: " .. newnick )
308	end
309
310	hub.getOwnSid = function( this )
311		if this._mySID then
312			return this._mySID
313		end
314		DC():PrintDebug( "[" .. this:getUrl() .."] Own SID not set, your scripts are failing. Reconnect please." )
315	end
316
317	hub.getOwnCid = function( this )
318		if this._myCID then
319			return this._myCID
320		end
321		DC():PrintDebug( "[" .. this:getUrl() .."] Own CID not set, your scripts are failing. Reconnect please." )
322	end
323
324	hub.getUser = function( this, sid, inf )
325		local newuser = false
326		if sid == "HUB9" and inf then
327			-- INF is about the hub itself
328			local params = {}
329			string.gsub(inf, "([^ ]+)", function(s) table.insert(params, s) end )
330			for k in pairs(params) do
331				local name = string.sub( params[k], 1, 2 )
332				if name == "NI" then
333					local NI = dcu:AdcEscape(string.sub( params[k], 3), true)
334					this._nick = NI
335					DC():PrintDebug("[STATUS] HUB nick set: " .. NI )
336				elseif name == "DE" then
337					local DE = dcu:AdcEscape(string.sub( params[k], 3), true)
338					this._description = DE
339					DC():PrintDebug("[STATUS] HUB description set: " .. DE )
340					this:addLine("The current hub topic is: " .. DE)
341				end
342			end
343		end
344
345		if not this._users[sid] then
346			this._users[sid] = createAdcUser( this, sid )
347			newuser = true
348			if this:getUptime() >= 2 then -- start sending messages AFTER logon
349	            for k,f in pairs(dcpp:getListeners( "adcUserCon" )) do
350					f( this, this._users[sid] )
351				end
352			end
353		end
354		local r = this._users[sid]
355		if inf then
356			local params = {}
357			string.gsub(inf, "([^ ]+)", function(s) table.insert(params, s) end )
358			for k in pairs(params) do
359				local name = string.sub( params[k], 1, 2 )
360				if name == "ID" then
361					local ID = string.sub( params[k], 3)
362					r:setCid(ID)
363					if sid == this._mySID then
364						this._myCID = ID
365						DC():PrintDebug("[STATUS] Own CID set: " .. ID )
366					end
367				elseif name == "CT" then
368					local CT = string.sub( params[k], 3)
369					r:processCt(CT)
370				elseif name == "I4" then
371					local I4 = string.sub( params[k], 3)
372					r:setIp(I4)
373				elseif name == "NI" then
374					local NI = dcu:AdcEscape(string.sub( params[k], 3), true)
375					if not newuser then
376						local oldNI = r:getNick()
377						r:setNick(NI)
378						this:addLine( oldNI .. " is now known as " .. NI )
379					else
380						r:setNick(NI)
381					end
382					if sid == this._mySID then
383						this:setOwnNick(NI)
384					end
385				end
386			end
387		end
388		return r
389	end
390
391	hub.removeUser = function( this, sid )
392		this._users[sid] = nil
393	end
394
395	hub.isOp = function( this, sid )
396		if this._users[sid] then
397			return this._users[sid]._op
398		end
399		return nil
400	end
401
402	hub.getSidbyNick = function( this, nick )
403		for k in pairs(this._users) do
404			if this._users[k]._nick == nick then
405				return k
406			end
407		end
408		return nil
409	end
410
411	hub.getSidbyCid = function( this, cid )
412		for k in pairs(this._users) do
413			if this._users[k]._cid == cid then
414				return k
415			end
416		end
417		return nil
418	end
419
420	hub.getUserByCid = function( this, cid )
421		for k in pairs(this._users) do
422			if this._users[k]._cid == cid then
423				return this._users[k]
424			end
425		end
426		return nil
427	end
428
429	hub.sendChat = function( this, msg )
430		local ownSid = this:getOwnSid()
431		msg = dcu:AdcEscape( msg )
432		DC():SendHubMessage( this:getId(), "BMSG " .. ownSid .. " " .. msg .. "\n" )
433	end
434
435	hub.injectChat = function( this, msg )
436			DC():PrintDebug("[WARNING] Your scripts trying to use hub:injectChat on ADC hub. Please use hub:injectMessage() to inject an ADC message or hub:addLine() to inject a chat line." )
437	end
438
439	hub.injectMessage = function( this, msg )
440		DC():InjectHubMessageADC( this:getId(), msg )
441	end
442
443	hub.addLine = function( this, msg, fmt )
444		--// TODO: need a function which adds a chat line without nick
445		msg = dcu:AdcEscape( msg )
446		DC():InjectHubMessageADC( this:getId(), "ISTA 000 " .. msg )
447	end
448
449	hub.sendPrivMsgTo = function( this, victimSid, msg_unescaped, hideFromSelf )
450		local ownSid = this:getOwnSid()
451		local msg = dcu:AdcEscape( msg_unescaped )
452		if ownSid then
453			if hideFromSelf then
454				DC():SendHubMessage( this:getId(), "DMSG ".. ownSid .. " " .. victimSid .." " .. msg .. " PM" .. ownSid .."\n" )
455			else
456				DC():SendHubMessage( this:getId(), "EMSG ".. ownSid .. " " .. victimSid .." " .. msg .. " PM" .. ownSid .."\n" )
457			end
458		end
459	end
460
461	hub.injectPrivMsg = function( this, victimSid, fromSid, msg )
462		DC():InjectHubMessageADC( this:getId(), "DMSG " .. fromSid .." ".. victimSid .." ".. msg .. " PM" .. victimSid .. "\n" )
463	end
464
465	hub.findUsers = function( this, nick, notag )
466		-- you get a possibly empty table of users
467		if not notag then
468			return { this._users[this:getSidbyNick(nick)] }
469		else
470			local list = {}
471			for k in pairs(this._users) do
472				local ret,c,n = string.find( this._users[k]._nick, "^%[.*%](.-)$" )
473				if n == nick then
474					table.insert( list, this._users[k] )
475				end
476			end
477			return list
478		end
479	end
480
481	hub.destroy = function( this )
482	end
483
484	--// events
485
486	hub.onChatMessage = function( this, user, text, me_msg )
487		local ret
488		for k,f in pairs(dcpp:getListeners( "adcChat" )) do
489			ret = f( this, user, text, me_msg, ret ) or ret
490		end
491		return ret
492	end
493
494	hub.onChatFromSelf = function( this, text, me_msg )
495		local ret
496		for k,f in pairs(dcpp:getListeners( "adcOwnChat" )) do
497			ret = f( this, text, me_msg, ret ) or ret
498		end
499		return ret
500	end
501
502	hub.onINF = function( this, user, flags )
503		local ret
504		for k,f in pairs(dcpp:getListeners( "userInf" )) do
505			ret = f( this, user, flags, ret ) or ret
506		end
507		return ret
508	end
509
510	hub.onQUI = function( this, sid, flags )
511		for k,f in pairs(dcpp:getListeners( "adcUserQui" )) do
512			f( this, sid, flags )
513		end
514	end
515
516	hub.onPrivateMessage = function( this, user, targetSid, replySid, text, me_msg )
517		if targetSid == this:getOwnSid() then
518			if user:getSid() == replySid then
519				local ret
520				for k,f in pairs(dcpp:getListeners( "adcPm" )) do
521					ret = f( this, user, text, me_msg, ret ) or ret
522				end
523				return ret
524			else
525				local ret
526				for k,f in pairs(dcpp:getListeners( "groupPm" )) do
527					ret = f( this, user, replySid, text, me_msg, ret ) or ret
528				end
529				return ret
530			end
531		end
532	end
533
534	hub.attention = function( this )
535		DC():HubWindowAttention( this:getId() )
536	end
537
538	return hub
539end
540
541function createNmdcHub( hubid )
542	local hub = {}
543
544	hub._id = hubid
545	hub._users = {}
546	hub._name = nil
547	hub._myNick = nil
548	hub._partial = nil
549	hub._gotOpList = nil
550	hub._uptime = os.time()
551	hub._hubUC = {}
552	hub._customUC = {}
553
554	hub.getId = function( this )
555		return this._id
556	end
557
558	hub.getProtocol = function( this )
559		return "nmdc"
560	end
561
562	hub.getUptime = function( this )
563		return ( os.time() - hub._uptime ) / 60
564	end
565
566	hub.getUser = function( this, nick, op )
567		if not this._users[nick] then
568			this._users[nick] = createNmdcUser( this, nick )
569			if this:getUptime() >= 2 then -- start sending messages AFTER logon
570	            for k,f in pairs(dcpp:getListeners( "userConnected" )) do
571					f( this, this._users[nick] )
572				end
573			end
574		end
575		local r = this._users[nick]
576		if op then
577			r:setOp( true )
578			r:setClass( "op" )
579			this._gotOpList = 1
580		end
581		return r
582	end
583
584	hub.findUsers = function( this, nick, notag )
585		-- you get a possibly empty table of users
586		if not notag then
587			return { this._users[nick] }
588		else
589			local list = {}
590			for k,v in pairs(this._users) do
591				local ret,c,n = string.find( k, "^%[.*%](.-)$" )
592				if n == nick then
593					table.insert( list, v )
594				end
595			end
596			return list
597		end
598	end
599
600	hub.gotOpList = function( this )
601		return this._gotOpList
602	end
603
604	hub.isOp = function( this, nick )
605		if this._users[nick] and this._users[nick]:isOp() then
606			return 1
607		end
608	end
609
610	hub.removeUser = function( this, nick )
611		this._users[nick] = nil
612	end
613
614	hub.setPartial = function( this )
615		--// we're most likely missing logon info
616		this._partial = 1
617	end
618
619	hub.isPartial = function( this )
620		--// are we missing logon info?
621		return this._partial
622	end
623
624	hub.setHubName = function( this, msg )
625		this._name = msg
626	end
627
628	hub.getAddress = function( this )
629		return DC():GetHubIpPort( this._id )
630	end
631
632	hub.getUrl = function( this )
633		return DC():GetHubUrl( this._id )
634	end
635
636	hub.getHubName = function( this )
637		if this._name then
638			return this._name.." ("..this:getUrl()..")"
639		else
640			return this:getUrl()
641		end
642	end
643
644	hub.setOwnNick = function( this, nick )
645		this._myNick = nick
646	end
647
648	hub.getOwnNick = function( this )
649		if not this:isPartial() then
650 			return this._myNick
651 		end
652		DC():PrintDebug( "[" .. this:getUrl() .."] Your scripts are failing. "..
653					"Own nick not set. Reconnect please." )
654	end
655
656	hub.destroy = function( this )
657	end
658
659	hub.sendChat = function( this, msg )
660		local ownNick = this:getOwnNick()
661		msg = dcu:NmdcEscape( msg )
662		if ownNick then
663			DC():SendHubMessage( this:getId(), "<"..ownNick.."> "..msg.."|" )
664		end
665	end
666
667	hub.injectChat = function( this, msg, skipusercommand )
668			DC():InjectHubMessage( this:getId(), msg )
669			-- test if it's usercommand
670			if ( string.sub(msg, 1, 13) == "$UserCommand " ) and (not skipusercommand) then
671				this:customUC( msg )
672			end
673	end
674
675	hub.addLine = function( this, msg, fmt )
676		if not fmt then
677			msg = "*** " .. msg
678		end
679		DC():InjectHubMessage( this:getId(), msg )
680	end
681
682	hub.sendPrivMsgTo = function( this, victim, msg, hideFromSelf )
683		local ownNick = this:getOwnNick()
684		msg = dcu:NmdcEscape( msg )
685		if ownNick then
686			DC():SendHubMessage( this:getId(), "$To: "..victim.." From: "..ownNick.." $"..msg.."|" )
687			if not hideFromSelf then
688				this:injectPrivMsg( victim, ownNick, msg )
689			end
690		end
691	end
692
693	hub.injectPrivMsg = function( this, from, to, msg )
694			DC():InjectHubMessage( this:getId(), "$To: "..to.." From: "..from.." $"..msg )
695	end
696
697	hub.injectPrivMsgFmt = function( this, from, to, msg )
698		this:injectPrivMsg( from, to, "<"..from.."> "..msg )
699	end
700
701	hub.attention = function( this )
702		-- TODO old method was broken, probably worthwhile functionality though
703	end
704
705	--///////////////////////////
706	--// $UserCommand manager
707	--///////////////////////////
708	-- Todo: context sensitivitiy at UC 255
709
710	hub.hubUC = function( this, msg )
711		local uc_type = string.gsub( msg, "%$UserCommand (%d+) .+", "%1")
712		-- DC():PrintDebug( "UserCommand " .. uc_type .. " arrived from " .. this:getHubName() .. " : " .. msg)
713		if uc_type == "255" then
714			-- clear user command list and resend our own commands to prevent it from disappearing
715			-- DC():PrintDebug( "Clearing Hub UC list.." )
716			this:clearHubUCList()
717			-- DC():PrintDebug( "Resending Custom commands.." )
718			this:reSendCustomUC()
719		else
720			-- add usercommands to UClist
721			-- DC():PrintDebug( "Adding hubUC.." )
722			this:addHubUC( msg )
723		end
724
725		return true
726	end
727
728	hub.customUC = function ( this, msg )
729		local uc_type = string.gsub( msg, "%$UserCommand (%d+) .+", "%1")
730		-- DC():PrintDebug( "UserCommand " .. uc_type .. " arrived from a script: " .. msg)
731		if uc_type == "255" then
732			-- clear own user command list and resend hub usercommands to prevent it from disappearing
733			this:clearCustomUCList()
734			this:reSendHubUC()
735		else
736			-- add usercommands to UClist
737			this:addCustomUC( msg )
738		end
739		return true
740	end
741
742	hub.addHubUC = function( this, msg )
743		table.insert( this._hubUC, msg )
744		return true
745	end
746
747	hub.addCustomUC = function( this, msg)
748		table.insert( this._customUC, msg )
749		return true
750	end
751
752	hub.clearHubUCList = function( this )
753		this._hubUC = {}
754		return true
755	end
756
757	hub.clearCustomUCList = function( this )
758		this._customUC = {}
759		return true
760	end
761
762	hub.reSendCustomUC = function( this )
763		for k in pairs(this._customUC) do
764			hub:injectChat( this._customUC[k] , true )
765			-- DC():PrintDebug("Sending custom uc from stored table: " .. this._customUC[k] )
766		end
767		return true
768	end
769
770	hub.reSendHubUC = function( this )
771		for k in pairs(this._hubUC) do
772			hub:injectChat( this._hubUC[k] , true)
773		end
774		return true
775	end
776
777	--////////////////
778	--// Own functions
779	--////////////////
780
781	hub.onRaw = function( this, msg )
782		local ret
783		for k,f in pairs(dcpp:getListeners( "raw" )) do
784			ret = f( this, msg ) or ret
785		end
786		return ret
787	end
788
789	hub.onSearch = function( this, msg )
790		--DC():PrintDebug( this:getHubName().."> "..msg )
791	end
792
793	hub.onHello = function( this, user, msg )
794	end
795
796	hub.onMyInfo = function( this, user, msg )
797		local ret
798		for k,f in pairs(dcpp:getListeners( "userMyInfo" )) do
799			ret = f( this, user, msg, ret ) or ret
800		end
801		return ret
802	end
803
804	hub.onQuit = function( this, nick, msg )
805		local ret
806		for k,f in pairs(dcpp:getListeners( "userQuit" )) do
807			ret = f( this, nick, ret ) or ret
808		end
809		return ret
810	end
811
812	hub.onHubName = function( this, hubname, msg )
813	end
814
815	hub.onPrivateMessage = function( this, user, to, prefix, text, full )
816		-- DC():PrintDebug("user: " .. tostring(user) .. " to: " .. tostring(to) .. " prefix: " .. tostring(prefix) .. " text: " .. tostring(text) .. " full: " .. tostring(full) )
817		if to == this:getOwnNick() then
818			if prefix == "<"..user:getNick().."> " then
819				local ret
820				for k,f in pairs(dcpp:getListeners( "pm" )) do
821					ret = f( this, user, text, ret ) or ret
822				end
823				return ret
824			elseif not prefix then
825				local ret
826				for k,f in pairs(dcpp:getListeners( "hubPm" )) do
827					ret = f( this, user, text, ret ) or ret
828				end
829				return ret
830			else
831				local ret
832				for k,f in pairs(dcpp:getListeners( "hubPm" )) do
833					ret = f( this, user, prefix .. text, ret ) or ret
834				end
835				return ret
836			end
837		end
838	end
839
840	hub.onChatMessage = function( this, user, text )
841		local ret
842		for k,f in pairs(dcpp:getListeners( "chat" )) do
843			ret = f( this, user, text, ret ) or ret
844		end
845		return ret
846	end
847
848	hub.onUnknownChatMessage = function( this, text )
849		local ret
850		for k,f in pairs(dcpp:getListeners( "unknownchat" )) do
851			ret = f( this, text, ret ) or ret
852		end
853		return ret
854	end
855
856	hub.onChatFromSelf = function( this, text )
857		local ret
858		for k,f in pairs(dcpp:getListeners( "ownChat" )) do
859			ret = f( this, text, ret ) or ret
860		end
861		return ret
862	end
863
864	return hub
865end
866
867
868--/////////////////////////////////////
869--// User object
870--/////////////////////////////////////
871
872function createNmdcUser( hub, nick )
873	local user = {}
874
875	user._hub = hub
876	user._nick = nick
877	user._op = false
878	user._ip = ""
879	user._class = "user"
880	user._handled_messages = {} -- flood protection
881
882	user.getProtocol = function( this )
883		return "nmdc"
884	end
885
886	user.setOp = function( this, op )
887		this._op = op
888		return this
889	end
890
891	user.isOp = function( this )
892		return this._op
893	end
894
895	user.setClass = function( this, param )
896		this._class = param
897		return this
898	end
899
900	user.getClass = function( this )
901		return this._class
902	end
903
904	user.setIp = function( this, ip )
905		this._ip = ip
906	end
907
908	user.getIp = function( this )
909		return this._ip
910	end
911
912	user.getNick = function( this )
913		return this._nick
914	end
915
916	user.sendPrivMsgFmt = function( this, msg, hideFromSelf )
917		local ownNick = this._hub:getOwnNick()
918		if ownNick then
919			this._hub:sendPrivMsgTo( this._nick, "<"..ownNick.."> "..msg, hideFromSelf )
920		end
921		return this
922	end
923
924	user.setMsgHandled = function( this, which )
925		this._handled_messages[which] = 1
926		return this
927	end
928
929	user.msgHandled = function( this, which )
930		return this._handled_messages[which]
931	end
932
933	return user
934end
935
936function createAdcUser( hub, sid )
937	local user = {}
938
939	user._sid = sid
940	user._cid = ""
941	user._hub = hub
942	user._nick = ""
943	user._op = false
944	user._bot = false
945	user._registered = false
946	user._hubitself = false
947	user._class = "user"
948	user._ip = ""
949	user._handled_messages = {} -- flood protection
950
951	user.getProtocol = function( this )
952		return "adc"
953	end
954
955	user.setOp = function( this, op )
956		this._op = op
957		return this
958	end
959
960	user.isOp = function( this )
961		return this._op
962	end
963
964	user.processCt = function( this, param )
965		local num = tonumber(param)
966		if num then
967
968			--// Init
969			this._class = "user"
970
971			--// 2^0: bot
972			if dcu:BBit(num, 0) then
973				this:setBot(true)
974				DC():PrintDebug("BOT set: " .. this:getSid())
975			else
976				this:setBot(false)
977			end
978
979			--// 2^1: registered
980			if dcu:BBit(num, 1) then
981				this:setReg(true)
982			else
983				this:setReg(false)
984			end
985
986			--// 2^2, 2^3, 2^4: some type of operator
987			if dcu:BBit(num, 2) or dcu:BBit(num, 3) or dcu:BBit(num, 4) then
988				this:setOp(true)
989				if dcu:BBit(num, 2) then
990					this._class = "op"
991				elseif dcu:BBit(num, 3) then
992					this._class = "su"
993				else
994					this._class = "owner"
995				end
996			else
997				this:setOp(false)
998			end
999
1000			if dcu:BBit(num, 5) then
1001				this:setHub(true)
1002				this._class = "hub"
1003			else
1004				this:setHub(false)
1005			end
1006
1007		else
1008			--// Empty CT field should cause all properties gone
1009			this:setBot(false)
1010			this:setOp(false)
1011			this:setReg(false)
1012			this:setHub(false)
1013			this._class = "user"
1014		end
1015
1016		--// DC():PrintDebug("CLASS: " .. this._class .. " [" .. tostring(param) .. "]" )
1017		return this
1018	end
1019
1020	--// Possible values: "user", "op", "su", "owner", "hub"
1021	user.getClass = function( this )
1022		return this._class
1023	end
1024
1025	user.setBot = function(this, bot)
1026		this._bot = bot
1027		return this
1028	end
1029
1030	user.isBot = function(this)
1031		return this._bot
1032	end
1033
1034	user.setReg = function(this, registered)
1035		this._registered = registered
1036		return this
1037	end
1038
1039	user.isReg = function(this)
1040		return this._registered
1041	end
1042
1043	user.setHub = function(this, hubitself)
1044		this._hubitself = hubitself
1045		return this
1046	end
1047
1048	user.isHub = function(this)
1049		return this._hubitself
1050	end
1051
1052	user.setIp = function( this, ip )
1053		this._ip = ip
1054	end
1055
1056	user.getIp = function( this )
1057		return this._ip
1058	end
1059
1060	user.setCid = function( this, cid )
1061		this._cid = cid
1062	end
1063
1064	user.getCid = function( this )
1065		return this._cid
1066	end
1067
1068	user.getSid = function( this )
1069		return this._sid
1070	end
1071
1072	user.getNick = function( this )
1073		return this._nick
1074	end
1075
1076	user.setNick = function( this, nick )
1077		this._nick = nick
1078		-- DC():PrintDebug("Nick set: " .. nick )
1079	end
1080
1081	user.sendPrivMsg = function( this, msg, hideFromSelf )
1082		local victimSid = this:getSid()
1083		this._hub:sendPrivMsgTo( victimSid, msg, hideFromSelf )
1084		return this
1085	end
1086
1087	-- Backward compatibility
1088	user.sendPrivMsgFmt = user.sendPrivMsg
1089
1090	user.setMsgHandled = function( this, which )
1091		this._handled_messages[which] = 1
1092		return this
1093	end
1094
1095	user.msgHandled = function( this, which )
1096		return this._handled_messages[which]
1097	end
1098
1099	return user
1100end
1101
1102
1103--/////////////////////////////////////
1104--// Handlers
1105--/////////////////////////////////////
1106
1107nmdch = {}
1108
1109function nmdch.DataArrival( hub, msg )
1110	local h = dcpp:getHub( hub )
1111
1112	--// If we missed the logon, we really have no business here,
1113	--// modify only if you really have to.
1114	--// Note that if not h:isPartial and not h:getOwnNick(),
1115	--// functions requiring an ownNick will silently fail.
1116	if h:isPartial() then
1117		return
1118	end
1119
1120	--// raw/unparsed message
1121	if h:onRaw( msg ) then
1122		return 1
1123	end
1124
1125	--// parse message and fire appropriate
1126	local ret,c,cmd = string.find( msg, "^%$([^ ]+)" )
1127	if ret then
1128		if cmd == "Search" then
1129			return h:onSearch( msg )
1130		elseif cmd == "Hello" then
1131			local nick = string.sub( msg, 8 )
1132			if not h:getOwnNick() then
1133				h:setOwnNick( nick ) -- don't trust this nick on h:isPartial()
1134			end
1135			return h:onHello( h:getUser( nick ), msg )
1136		elseif cmd == "MyINFO" and string.sub( msg, 1, 13 ) == "$MyINFO $ALL " then
1137			local nick = string.sub( msg, 14, string.find( msg, " ", 14, 1 ) - 1 )
1138			return h:onMyInfo( h:getUser( nick ), msg )
1139		elseif cmd == "Quit" then
1140			local nick = string.sub( msg, 7 )
1141			h:removeUser( nick )
1142			return h:onQuit( nick, msg )
1143		elseif cmd == "HubName" then
1144			local hubname = string.sub( msg, 10 )
1145			h:setHubName( hubname )
1146			return h:onHubName( hubname, msg )
1147		elseif cmd == "OpList" then
1148			for nick in string.gfind( string.sub( msg, 9 ), "[^$]+") do
1149				h:getUser( nick, 1 )
1150			end
1151			return nil
1152		elseif cmd == "UserIP" then
1153			local nick,ip
1154			for combo in string.gfind( string.sub( msg, 9 ), "[^$]+") do
1155				ret,c,nick,ip = string.find( combo, "^(%S+) (%S+)$" )
1156				if ret then
1157					h:getUser( nick ):setIp( ip )
1158				end
1159			end
1160			return nil
1161		elseif cmd == "UserCommand" then
1162			h:hubUC( msg )
1163		--elseif string.sub( msg, 1, 10 ) == "$NickList " then
1164		--	for nick in string.gfind( string.sub( msg, 9, -1), "[^$]+") do
1165		--		h:getUser( nick )
1166		--	end
1167		--	return nil
1168		elseif cmd == "To:" then
1169			local ret,c,to,from,fulltext = string.find( msg, "^%$To: ([^ ]+) From: ([^ ]+) %$(.*)$" )
1170			if ret then
1171				local ret,c,prefix,text = string.find( fulltext, "^(%b<> )(.*)$" )
1172				if ret then
1173					return h:onPrivateMessage( h:getUser( from ), to, prefix, text, msg )
1174				else
1175					return h:onPrivateMessage( h:getUser( from ), to, nil, fulltext, msg )
1176				end
1177			end
1178		end
1179	elseif string.sub( msg, 1, 1 ) == "<" then
1180		local ret,c,nick,text = string.find( msg, "^<([^>]+)> (.*)$" )
1181		if ret and h:getOwnNick() then
1182			if nick ~= h:getOwnNick() then -- don't be flooding mainchat now..
1183 				return h:onChatMessage( h:getUser( nick ), text )
1184			else
1185				return h:onChatFromSelf( text )
1186			end
1187		end
1188	elseif msg ~= "" then
1189		return h:onUnknownChatMessage(msg)
1190	end
1191end
1192
1193function dcpp.UserDataIn( user, msg )
1194	local ret
1195	for k,f in pairs(dcpp:getListeners( "clientIn" )) do
1196		ret = f( user, msg ) or ret
1197	end
1198	return ret
1199end
1200
1201function dcpp.UserDataOut( user, msg )
1202	local ret
1203	for k,f in pairs(dcpp:getListeners( "clientOut" )) do
1204		ret = f( user, msg ) or ret
1205	end
1206	return ret
1207end
1208
1209function dcpp.OnCommandEnter( hub, text )
1210	local h = dcpp:getHub( hub )
1211	local ret
1212	for k,f in pairs(dcpp:getListeners( "ownChatOut" )) do
1213		ret = f( h, text, ret ) or ret
1214	end
1215	return ret
1216end
1217
1218function dcpp.OnTimer()
1219	-- Called every second.
1220	for k,f in pairs(dcpp:getListeners( "timer" )) do
1221		f()
1222	end
1223end
1224
1225function nmdch.OnHubAdded( hub )
1226	dcpp:addHub( hub, false )
1227end
1228
1229function nmdch.OnHubRemoved( hub )
1230	dcpp:removeHub( hub )
1231end
1232
1233adch = {}
1234
1235function adch.DataArrival( hub, msg )
1236	if msg == "" then
1237		return nil
1238	end
1239	local h = dcpp:getHub( hub )
1240
1241	local params = {}
1242	string.gsub(msg, "([^ ]+)", function(s) table.insert(params, s) end )
1243
1244	local mtype = string.sub(params[1], 1, 1)
1245	local cmd = string.sub(params[1], 2, 4)
1246	local sid, targetSid, parameters = false, false, ""
1247
1248	if mtype == "I" then
1249		parameters = string.sub( msg, 6)
1250		sid = "HUB9"
1251	elseif mtype == "B" then
1252		parameters = string.sub( msg, 11)
1253		sid = params[2]
1254	elseif mtype == "D" or mtype == "E" then
1255		parameters = string.sub( msg, 16 )
1256		sid = params[2]
1257		targetSid = params[3]
1258	end
1259
1260	-- Building flags table
1261	local flags = {}
1262	string.gsub(parameters, "([^ ]+)", function(s) flags[string.sub(s, 1, 2)] = dcu:AdcEscape( string.sub(s, 3), true ) end )
1263
1264	if cmd == "SID" then
1265		DC():PrintDebug( "[STATUS] SID received: " .. params[2] )
1266		h._mySID = params[2]
1267	elseif cmd == "QUI" then
1268		h:removeUser( params[2] )
1269		h:onQUI( params[2], flags )
1270	elseif cmd == "INF" then
1271		local u = h:getUser( sid, parameters )
1272		return h:onINF( u, flags )
1273	elseif cmd == "MSG" then
1274		local pm, replySid, me_msg = false, false, false
1275		local tmp = {}
1276
1277		string.gsub(parameters, "([^ ]+)", function(s) table.insert(tmp, s) end )
1278		local text = dcu:AdcEscape(tmp[1], true)
1279		tmp[1] = nil
1280
1281		-- Check for named parameters
1282		for k in pairs(tmp) do
1283			local name = string.sub(tmp[k], 1,2)
1284			local value = string.sub(tmp[k], 3)
1285			if name == "ME" then
1286				if value == "1" then
1287					me_msg = true
1288				end
1289			elseif name == "PM" then
1290				pm = true
1291				replySid = value
1292			end
1293		end
1294
1295		if pm then
1296			return h:onPrivateMessage( h:getUser( sid ), targetSid, replySid, text, me_msg )
1297		else
1298			if sid ~= h:getOwnSid() then -- don't be flooding mainchat now..
1299				return h:onChatMessage( h:getUser( sid ), text, me_msg )
1300			else
1301				return h:onChatFromSelf( text, me_msg )
1302			end
1303		end
1304
1305	end
1306end
1307
1308function adch.OnHubAdded( hub )
1309	dcpp:addHub( hub, true )
1310end
1311
1312function adch.OnHubRemoved( hub )
1313	dcpp:removeHub( hub )
1314end
1315
1316--/////////////////////////////////////
1317--// Utility functions
1318--/////////////////////////////////////
1319
1320function SendActiveSearchResult( hub, ip_port, search_nick, filename, filesize, open_slots,
1321		total_slots )
1322	DC():SendUDP( ip_port, "$SR "..search_nick.." "..filename.."\005".. filesize.." "..open_slots..
1323			"/"..total_slots.."\005"..dcpp:getHub( hub ):getHubName() ) -- no pipe in UDP $SR
1324end
1325
1326
1327--/////////////////////////////////////
1328--// Execute your own scripts
1329--/////////////////////////////////////
1330
1331-- do you need the timer?
1332--DC():RunTimer(1)
1333
1334--dofile( DC():GetScriptsPath() .. "bier.lua" )
1335--dofile( DC():GetScriptsPath() .. "slots.lua" )
1336--dofile( DC():GetScriptsPath() .. "formatting.lua" )
1337dofile( DC():GetScriptsPath() .. "uptime.lua" )
1338--dofile( DC():GetScriptsPath() .. "onjoin.lua" )
1339--dofile( DC():GetScriptsPath() .. "monologue.lua" )
1340dofile( DC():GetScriptsPath() .. "ignore.lua" )
1341--dofile( DC():GetScriptsPath() .. "p2pblock.lua" )
1342--dofile( DC():GetScriptsPath() .. "quiet_login.lua" )
1343--dofile( DC():GetScriptsPath() .. "log.lua" )
1344--dofile( DC():GetScriptsPath() .. "kickfilter.lua" )
1345dofile( DC():GetScriptsPath() .. "adccommands.lua" )
1346