xref: /haiku/src/add-ons/print/transports/ipp/Ipp.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 // Sun, 18 Jun 2000
2 // Y.Takagi
3 
4 #include "IppTransport.h"
5 #include "DbgMsg.h"
6 
7 #include <PrintTransportAddOn.h>
8 #include <NetEndpoint.h>
9 #include <String.h>
10 #include <OS.h>
11 
12 #include <HashString.h>
13 #include <HashMap.h>
14 
15 IppTransport *transport = NULL;
16 
17 // Set transport_features so we stay loaded
18 uint32 transport_features = B_TRANSPORT_IS_HOTPLUG | B_TRANSPORT_IS_NETWORK;
19 
20 
21 class IPPPrinter {
22 public:
23 	IPPPrinter(const BString& uri, uint32 type)
24 		{ fURI=uri; fType=type; }
25 
26 	uint32 fType, fState;
27 	BString fURI, fLocation, fInfo, fMakeModel, fAttributes;
28 };
29 
30 
31 class IPPPrinterRoster {
32 public:
33 	IPPPrinterRoster();
34 	~IPPPrinterRoster();
35 
36 	status_t ListPorts(BMessage *msg);
37 	status_t Listen();
38 private:
39 	char *_ParseString(BString& outStr, char*& pos);
40 
41 	static status_t _IPPListeningThread(void *cookie);
42 
43 	typedef HashMap<HashString,IPPPrinter*> IPPPrinterMap;
44 	IPPPrinterMap fPrinters;
45 	BNetEndpoint *fEndpoint;
46 	thread_id fListenThreadID;
47 };
48 
49 
50 static IPPPrinterRoster gRoster;
51 
52 
53 IPPPrinterRoster::IPPPrinterRoster()
54 {
55 	// Setup our (UDP) listening endpoint
56 	fEndpoint = new BNetEndpoint(SOCK_DGRAM);
57 	if (!fEndpoint)
58 		return;
59 
60 	if (fEndpoint->InitCheck() != B_OK)
61 		return;
62 
63 	if (fEndpoint->Bind(BNetAddress(INADDR_ANY, 631)) != B_OK)
64 		return;
65 
66 	// Now create thread for listening
67 	fListenThreadID = spawn_thread(_IPPListeningThread, "IPPListener", B_LOW_PRIORITY, this);
68 	if (fListenThreadID <= 0)
69 		return;
70 
71 	resume_thread(fListenThreadID);
72 }
73 
74 
75 IPPPrinterRoster::~IPPPrinterRoster()
76 {
77 	kill_thread(fListenThreadID);
78 	delete fEndpoint;
79 }
80 
81 
82 status_t
83 IPPPrinterRoster::Listen()
84 {
85 	BNetAddress srcAddress;
86 	uint32 type, state;
87 	char packet[1541];
88 	char uri[1024];
89 	char* pos;
90 	int32 len;
91 
92 	while ((len=fEndpoint->ReceiveFrom(packet, sizeof(packet) -1, srcAddress)) > 0) {
93 		packet[len] = '\0';
94 
95 		// Verify packet format
96 		if (sscanf(packet, "%" B_SCNx32 "%" B_SCNx32 "%1023s", &type, &state,
97 			uri) == 3) {
98 			IPPPrinter *printer = fPrinters.Get(uri);
99 			if (!printer) {
100 				printer = new IPPPrinter(uri, type);
101 				fPrinters.Put(printer->fURI.String(), printer);
102 			}
103 
104 			printer->fState=state;
105 
106 			// Check for option parameters
107 			if ((pos=strchr(packet, '"')) != NULL) {
108 				BString str;
109 				if (_ParseString(str, pos))
110 					printer->fLocation = str;
111 				if (pos && _ParseString(str, pos))
112 					printer->fInfo = str;
113 				if (pos && _ParseString(str, pos))
114 					printer->fMakeModel = str;
115 
116 				if (pos)
117 					printer->fAttributes = pos;
118 			}
119 
120 			DBGMSG(("Printer: %s\nLocation: %s\nInfo: %s\nMakeModel: %s\nAttributes: %s\n",
121 				printer->fURI.String(), printer->fLocation.String(), printer->fInfo.String(),
122 				printer->fMakeModel.String(), printer->fAttributes.String()));
123 		}
124 	}
125 
126 	return len;
127 }
128 
129 
130 status_t
131 IPPPrinterRoster::ListPorts(BMessage* msg)
132 {
133 	IPPPrinterMap::Iterator iterator = fPrinters.GetIterator();
134 	while (iterator.HasNext()) {
135 		const IPPPrinterMap::Entry& entry = iterator.Next();
136 		msg->AddString("port_id", entry.value->fURI);
137 
138 		BString name = entry.value->fInfo;
139 		if (name.Length() && entry.value->fLocation.Length()) {
140 			name.Append(" [");
141 			name.Append(entry.value->fLocation);
142 			name.Append("]");
143 		}
144 
145 		msg->AddString("port_name", name);
146 	}
147 
148 	return B_OK;
149 }
150 
151 
152 char*
153 IPPPrinterRoster::_ParseString(BString& outStr, char*& pos)
154 {
155 	outStr = "";
156 
157 	if (*pos == '"')
158 		pos++;
159 
160 	while(*pos && *pos != '"')
161 		outStr.Append(*pos++, 1);
162 
163 	if (*pos == '"')
164 		++pos;
165 
166 	while(*pos && isspace(*pos))
167 		++pos;
168 
169 	if (!*pos)
170 		pos = NULL;
171 
172 	return pos;
173 }
174 
175 
176 status_t
177 IPPPrinterRoster::_IPPListeningThread(void *cookie)
178 {
179 	return ((IPPPrinterRoster*)cookie)->Listen();
180 }
181 
182 
183 // --- general transport hooks
184 
185 extern "C" _EXPORT void
186 exit_transport()
187 {
188 	DBGMSG(("> exit_transport\n"));
189 	if (transport) {
190 		delete transport;
191 		transport = NULL;
192 	}
193 	DBGMSG(("< exit_transport\n"));
194 }
195 
196 
197 // List detected printers
198 extern "C" _EXPORT status_t
199 list_transport_ports(BMessage* msg)
200 {
201 	return gRoster.ListPorts(msg);
202 }
203 
204 
205 extern "C" _EXPORT BDataIO *
206 init_transport(BMessage *msg)
207 {
208 	DBGMSG(("> init_transport\n"));
209 
210 	transport = new IppTransport(msg);
211 
212 	if (transport->fail()) {
213 		exit_transport();
214 	}
215 
216 	if (msg)
217 		msg->what = 'okok';
218 
219 	DBGMSG(("< init_transport\n"));
220 	return transport;
221 }
222