1 /*
2  * Copyright (c) 2005 William Pitcock, et al.
3  * Rights to this code are as documented in doc/LICENSE.
4  *
5  * This file contains code for the CService INFO functions.
6  *
7  */
8 
9 #include "atheme.h"
10 
11 DECLARE_MODULE_V1
12 (
13 	"chanserv/info", false, _modinit, _moddeinit,
14 	PACKAGE_STRING,
15 	VENDOR_STRING
16 );
17 
18 static void cs_cmd_info(sourceinfo_t *si, int parc, char *parv[]);
19 
20 command_t cs_info = { "INFO", N_("Displays information on registrations."),
21                         AC_NONE, 2, cs_cmd_info, { .path = "cservice/info" } };
22 
_modinit(module_t * m)23 void _modinit(module_t *m)
24 {
25         service_named_bind_command("chanserv", &cs_info);
26 }
27 
_moddeinit(module_unload_intent_t intent)28 void _moddeinit(module_unload_intent_t intent)
29 {
30 	service_named_unbind_command("chanserv", &cs_info);
31 }
32 
cs_cmd_info(sourceinfo_t * si,int parc,char * parv[])33 static void cs_cmd_info(sourceinfo_t *si, int parc, char *parv[])
34 {
35 	mychan_t *mc;
36 	char *name = parv[0];
37 	char buf[BUFSIZE], strfbuf[BUFSIZE];
38 	struct tm tm;
39 	myuser_t *mu;
40 	metadata_t *md;
41 	mowgli_patricia_iteration_state_t state;
42 	hook_channel_req_t req;
43 	bool hide_info, hide_acl;
44 
45 	if (!name)
46 	{
47 		command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "INFO");
48 		command_fail(si, fault_needmoreparams, _("Syntax: INFO <#channel>"));
49 		return;
50 	}
51 
52 	if (*name != '#')
53 	{
54 		command_fail(si, fault_badparams, STR_INVALID_PARAMS, "INFO");
55 		command_fail(si, fault_badparams, _("Syntax: INFO <#channel>"));
56 		return;
57 	}
58 
59 	if (!(mc = mychan_find(name)))
60 	{
61 		command_fail(si, fault_nosuch_target, _("Channel \2%s\2 is not registered."), name);
62 		return;
63 	}
64 
65 	if (!has_priv(si, PRIV_CHAN_AUSPEX) && metadata_find(mc, "private:close:closer"))
66 	{
67 		command_fail(si, fault_noprivs, _("\2%s\2 has been closed down by the %s administration."), mc->name, me.netname);
68 		return;
69 	}
70 
71 	hide_info = use_channel_private && mc->flags & MC_PRIVATE &&
72 		!chanacs_source_has_flag(mc, si, CA_ACLVIEW) &&
73 		!has_priv(si, PRIV_CHAN_AUSPEX);
74 	hide_acl = !chanacs_source_has_flag(mc, si, CA_ACLVIEW) &&
75 		!has_priv(si, PRIV_CHAN_AUSPEX) &&
76 		!(mc->flags & MC_PUBACL);
77 
78 	tm = *localtime(&mc->registered);
79 	strftime(strfbuf, sizeof strfbuf, TIME_FORMAT, &tm);
80 
81 	command_success_nodata(si, _("Information on \2%s\2:"), mc->name);
82 
83 	if (!(hide_info && hide_acl))
84 		command_success_nodata(si, _("Founder    : %s"), mychan_founder_names(mc));
85 
86 	if (!hide_acl)
87 	{
88 		mu = mychan_pick_successor(mc);
89 		if (mu != NULL)
90 			command_success_nodata(si, _("Successor  : %s"), entity(mu)->name);
91 		else
92 			command_success_nodata(si, _("Successor  : (none)"));
93 	}
94 
95 	command_success_nodata(si, _("Registered : %s (%s ago)"), strfbuf, time_ago(mc->registered));
96 
97 	if (CURRTIME - mc->used >= 86400)
98 	{
99 		if (hide_info)
100 			command_success_nodata(si, _("Last used  : (about %d weeks ago)"), (int)((CURRTIME - mc->used) / 604800));
101 		else
102 		{
103 			tm = *localtime(&mc->used);
104 			strftime(strfbuf, sizeof strfbuf, TIME_FORMAT, &tm);
105 			command_success_nodata(si, _("Last used  : %s (%s ago)"), strfbuf, time_ago(mc->used));
106 		}
107 	}
108 
109 	md = metadata_find(mc, "private:mlockext");
110 	if (mc->mlock_on || mc->mlock_off || mc->mlock_limit || mc->mlock_key || md)
111 	{
112 		if (md != NULL && strlen(md->value) > 450)
113 		{
114 			/* Be safe */
115 			command_success_nodata(si, _("Mode lock is too long, not entirely shown"));
116 			md = NULL;
117 		}
118 
119 		command_success_nodata(si, _("Mode lock  : %s"), mychan_get_mlock(mc));
120 	}
121 
122 
123 	if ((!hide_info || (si->su != NULL && chanuser_find(mc->chan, si->su))) &&
124 			(md = metadata_find(mc, "url")))
125 		command_success_nodata(si, "URL        : %s", md->value);
126 
127 	if (!hide_info && (md = metadata_find(mc, "email")))
128 		command_success_nodata(si, "Email      : %s", md->value);
129 
130 	if ((!hide_info || (si->su != NULL && chanuser_find(mc->chan, si->su))) &&
131 			(md = metadata_find(mc, "private:entrymsg")))
132 		command_success_nodata(si, "Entrymsg   : %s", md->value);
133 
134 	if (!hide_info)
135 	{
136 		MOWGLI_PATRICIA_FOREACH(md, &state, object(mc)->metadata)
137 		{
138 			if (!strncmp(md->name, "private:", 8))
139 				continue;
140 			/* these are shown separately */
141 			if (!strcasecmp(md->name, "email") ||
142 					!strcasecmp(md->name, "url") ||
143 					!strcasecmp(md->name, "disable_fantasy"))
144 				continue;
145 			command_success_nodata(si, _("Metadata   : %s = %s"),
146 					md->name, md->value);
147 		}
148 	}
149 
150 	*buf = '\0';
151 
152 	if (MC_HOLD & mc->flags)
153 		strcat(buf, "HOLD");
154 
155 	if (MC_SECURE & mc->flags)
156 	{
157 		if (*buf)
158 			strcat(buf, " ");
159 
160 		strcat(buf, "SECURE");
161 	}
162 
163 	if (MC_VERBOSE & mc->flags)
164 	{
165 		if (*buf)
166 			strcat(buf, " ");
167 
168 		strcat(buf, "VERBOSE");
169 	}
170 	if (MC_VERBOSE_OPS & mc->flags)
171 	{
172 		if (*buf)
173 			strcat(buf, " ");
174 
175 		strcat(buf, "VERBOSE_OPS");
176 	}
177 
178 	if (MC_RESTRICTED & mc->flags)
179 	{
180 		if (*buf)
181 			strcat(buf, " ");
182 
183 		strcat(buf, "RESTRICTED");
184 	}
185 
186 	if (MC_KEEPTOPIC & mc->flags)
187 	{
188 		if (*buf)
189 			strcat(buf, " ");
190 
191 		strcat(buf, "KEEPTOPIC");
192 	}
193 
194 	if (MC_TOPICLOCK & mc->flags)
195 	{
196 		if (*buf)
197 			strcat(buf, " ");
198 
199 		strcat(buf, "TOPICLOCK");
200 	}
201 
202 	if (MC_GUARD & mc->flags)
203 	{
204 		if (*buf)
205 			strcat(buf, " ");
206 
207 		strcat(buf, "GUARD");
208 	}
209 
210 	if (MC_ANTIFLOOD & mc->flags)
211 	{
212 		if (*buf)
213 			strcat(buf, " ");
214 
215 		strcat(buf, "ANTIFLOOD");
216 	}
217 
218 	if (chansvs.fantasy && !metadata_find(mc, "disable_fantasy"))
219 	{
220 		if (*buf)
221 			strcat(buf, " ");
222 
223 		strcat(buf, "FANTASY");
224 	}
225 
226 	if (MC_NOSYNC & mc->flags)
227 	{
228 		if (*buf)
229 			strcat(buf, " ");
230 
231 		strcat(buf, "NOSYNC");
232 	}
233 
234 	if (use_channel_private && MC_PRIVATE & mc->flags)
235 	{
236 		if (*buf)
237 			strcat(buf, " ");
238 
239 		strcat(buf, "PRIVATE");
240 	}
241 
242 	if (MC_PUBACL & mc->flags)
243 	{
244 		if (*buf)
245 			strcat(buf, " ");
246 
247 		strcat(buf, "PUBACL");
248 	}
249 
250 	if (use_limitflags && MC_LIMITFLAGS & mc->flags)
251 	{
252 		if (*buf)
253 			strcat(buf, " ");
254 
255 		strcat(buf, "LIMITFLAGS");
256 	}
257 
258 	if (*buf)
259 		command_success_nodata(si, _("Flags      : %s"), buf);
260 
261 	if (chansvs.fantasy && !metadata_find(mc, "disable_fantasy"))
262 	{
263 		if ((md = metadata_find(mc, "private:prefix")))
264 			command_success_nodata(si, _("Prefix     : %s"), md->value);
265 		else
266 			command_success_nodata(si, _("Prefix     : %s (default)"), chansvs.trigger);
267 	}
268 
269 	if (has_priv(si, PRIV_CHAN_AUSPEX) && (md = metadata_find(mc, "private:mark:setter")))
270 	{
271 		const char *setter = md->value;
272 		const char *reason;
273 		time_t ts;
274 
275 		md = metadata_find(mc, "private:mark:reason");
276 		reason = md != NULL ? md->value : "unknown";
277 
278 		md = metadata_find(mc, "private:mark:timestamp");
279 		ts = md != NULL ? atoi(md->value) : 0;
280 
281 		tm = *localtime(&ts);
282 		strftime(strfbuf, sizeof strfbuf, TIME_FORMAT, &tm);
283 
284 		command_success_nodata(si, _("%s was \2MARKED\2 by %s on %s (%s)"), mc->name, setter, strfbuf, reason);
285 	}
286 
287 	if (has_priv(si, PRIV_CHAN_AUSPEX) && (MC_INHABIT & mc->flags))
288 		command_success_nodata(si, _("%s is temporarily holding this channel."), chansvs.nick);
289 
290 	if (has_priv(si, PRIV_CHAN_AUSPEX) && (md = metadata_find(mc, "private:close:closer")))
291 	{
292 		const char *setter = md->value;
293 		const char *reason;
294 		time_t ts;
295 
296 		md = metadata_find(mc, "private:close:reason");
297 		reason = md != NULL ? md->value : "unknown";
298 
299 		md = metadata_find(mc, "private:close:timestamp");
300 		ts = md != NULL ? atoi(md->value) : 0;
301 
302 		tm = *localtime(&ts);
303 		strftime(strfbuf, sizeof strfbuf, TIME_FORMAT, &tm);
304 
305 		command_success_nodata(si, _("%s was \2CLOSED\2 by %s on %s (%s)"), mc->name, setter, strfbuf, reason);
306 	}
307 
308 	req.mc = mc;
309 	req.si = si;
310 	hook_call_channel_info(&req);
311 
312 	command_success_nodata(si, _("\2*** End of Info ***\2"));
313 	logcommand(si, CMDLOG_GET, "INFO: \2%s\2", mc->name);
314 }
315 
316 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
317  * vim:ts=8
318  * vim:sw=8
319  * vim:noexpandtab
320  */
321