2 * Copyright 2007 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "urlmon_main.h"
20 #include "wine/debug.h"
22 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
24 typedef struct BindProtocol BindProtocol;
26 struct _task_header_t;
28 typedef void (*task_proc_t)(BindProtocol*,struct _task_header_t*);
30 typedef struct _task_header_t {
32 struct _task_header_t *next;
36 const IInternetProtocolVtbl *lpInternetProtocolVtbl;
37 const IInternetBindInfoVtbl *lpInternetBindInfoVtbl;
38 const IInternetPriorityVtbl *lpInternetPriorityVtbl;
39 const IServiceProviderVtbl *lpServiceProviderVtbl;
40 const IInternetProtocolSinkVtbl *lpInternetProtocolSinkVtbl;
44 IInternetProtocol *protocol;
45 IInternetBindInfo *bind_info;
46 IInternetProtocolSink *protocol_sink;
47 IServiceProvider *service_provider;
48 IWinInetInfo *wininet_info;
56 DWORD apartment_thread;
60 CRITICAL_SECTION section;
61 task_header_t *task_queue_head, *task_queue_tail;
64 #define PROTOCOL(x) ((IInternetProtocol*) &(x)->lpInternetProtocolVtbl)
65 #define BINDINFO(x) ((IInternetBindInfo*) &(x)->lpInternetBindInfoVtbl)
66 #define PRIORITY(x) ((IInternetPriority*) &(x)->lpInternetPriorityVtbl)
67 #define SERVPROV(x) ((IServiceProvider*) &(x)->lpServiceProviderVtbl)
68 #define PROTSINK(x) ((IInternetProtocolSink*) &(x)->lpInternetProtocolSinkVtbl)
70 void handle_bindprot_task(void *v)
72 BindProtocol *This = v;
76 EnterCriticalSection(&This->section);
78 task = This->task_queue_head;
80 This->task_queue_head = task->next;
81 if(!This->task_queue_head)
82 This->task_queue_tail = NULL;
85 LeaveCriticalSection(&This->section);
90 This->continue_call++;
91 task->proc(This, task);
92 This->continue_call--;
95 IInternetProtocol_Release(PROTOCOL(This));
98 static void push_task(BindProtocol *This, task_header_t *task, task_proc_t proc)
100 BOOL do_post = FALSE;
105 EnterCriticalSection(&This->section);
107 if(This->task_queue_tail) {
108 This->task_queue_tail->next = task;
109 This->task_queue_tail = task;
111 This->task_queue_tail = This->task_queue_head = task;
115 LeaveCriticalSection(&This->section);
118 IInternetProtocol_AddRef(PROTOCOL(This));
119 PostMessageW(This->notif_hwnd, WM_MK_CONTINUE2, 0, (LPARAM)This);
123 static BOOL inline do_direct_notif(BindProtocol *This)
125 return !(This->pi & PI_APARTMENTTHREADED) || (This->apartment_thread == GetCurrentThreadId() && !This->continue_call);
128 #define PROTOCOL_THIS(iface) DEFINE_THIS(BindProtocol, InternetProtocol, iface)
130 static HRESULT WINAPI BindProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
132 BindProtocol *This = PROTOCOL_THIS(iface);
135 if(IsEqualGUID(&IID_IUnknown, riid)) {
136 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
137 *ppv = PROTOCOL(This);
138 }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
139 TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
140 *ppv = PROTOCOL(This);
141 }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
142 TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
143 *ppv = PROTOCOL(This);
144 }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
145 TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
146 *ppv = BINDINFO(This);
147 }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
148 TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
149 *ppv = PRIORITY(This);
150 }else if(IsEqualGUID(&IID_IAuthenticate, riid)) {
151 FIXME("(%p)->(IID_IAuthenticate %p)\n", This, ppv);
152 }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
153 TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
154 *ppv = SERVPROV(This);
155 }else if(IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
156 TRACE("(%p)->(IID_IInternetProtocolSink %p)\n", This, ppv);
157 *ppv = PROTSINK(This);
161 IInternetProtocol_AddRef(iface);
165 WARN("not supported interface %s\n", debugstr_guid(riid));
166 return E_NOINTERFACE;
169 static ULONG WINAPI BindProtocol_AddRef(IInternetProtocol *iface)
171 BindProtocol *This = PROTOCOL_THIS(iface);
172 LONG ref = InterlockedIncrement(&This->ref);
173 TRACE("(%p) ref=%d\n", This, ref);
177 static ULONG WINAPI BindProtocol_Release(IInternetProtocol *iface)
179 BindProtocol *This = PROTOCOL_THIS(iface);
180 LONG ref = InterlockedDecrement(&This->ref);
182 TRACE("(%p) ref=%d\n", This, ref);
185 if(This->wininet_info)
186 IWinInetInfo_Release(This->wininet_info);
188 IInternetProtocol_Release(This->protocol);
190 IInternetBindInfo_Release(This->bind_info);
192 set_binding_sink(PROTOCOL(This), NULL);
195 release_notif_hwnd(This->notif_hwnd);
196 DeleteCriticalSection(&This->section);
199 URLMON_UnlockModule();
205 static HRESULT WINAPI BindProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
206 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
207 DWORD grfPI, HANDLE_PTR dwReserved)
209 BindProtocol *This = PROTOCOL_THIS(iface);
210 IInternetProtocol *protocol = NULL;
211 IInternetPriority *priority;
212 IServiceProvider *service_provider;
213 BOOL urlmon_protocol = FALSE;
214 CLSID clsid = IID_NULL;
218 TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
219 pOIBindInfo, grfPI, dwReserved);
221 if(!szUrl || !pOIProtSink || !pOIBindInfo)
226 hres = IInternetProtocolSink_QueryInterface(pOIProtSink, &IID_IServiceProvider,
227 (void**)&service_provider);
228 if(SUCCEEDED(hres)) {
229 /* FIXME: What's protocol CLSID here? */
230 IServiceProvider_QueryService(service_provider, &IID_IInternetProtocol,
231 &IID_IInternetProtocol, (void**)&protocol);
232 IServiceProvider_Release(service_provider);
239 hres = get_protocol_handler(szUrl, &clsid, &urlmon_protocol, &cf);
243 if(This->from_urlmon) {
244 hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&protocol);
245 IClassFactory_Release(cf);
249 hres = IClassFactory_CreateInstance(cf, (IUnknown*)BINDINFO(This),
250 &IID_IUnknown, (void**)&unk);
251 IClassFactory_Release(cf);
255 hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocol, (void**)&protocol);
256 IUnknown_Release(unk);
262 StringFromCLSID(&clsid, &clsid_str);
263 IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_PROTOCOLCLASSID, clsid_str);
264 CoTaskMemFree(clsid_str);
266 This->protocol = protocol;
269 IInternetProtocol_QueryInterface(protocol, &IID_IWinInetInfo, (void**)&This->wininet_info);
271 IInternetBindInfo_AddRef(pOIBindInfo);
272 This->bind_info = pOIBindInfo;
274 set_binding_sink(PROTOCOL(This), pOIProtSink);
276 hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetPriority, (void**)&priority);
277 if(SUCCEEDED(hres)) {
278 IInternetPriority_SetPriority(priority, This->priority);
279 IInternetPriority_Release(priority);
282 return IInternetProtocol_Start(protocol, szUrl, PROTSINK(This), BINDINFO(This), 0, 0);
285 static HRESULT WINAPI BindProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
287 BindProtocol *This = PROTOCOL_THIS(iface);
289 TRACE("(%p)->(%p)\n", This, pProtocolData);
291 return IInternetProtocol_Continue(This->protocol, pProtocolData);
294 static HRESULT WINAPI BindProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
297 BindProtocol *This = PROTOCOL_THIS(iface);
298 FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
302 static HRESULT WINAPI BindProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
304 BindProtocol *This = PROTOCOL_THIS(iface);
306 TRACE("(%p)->(%08x)\n", This, dwOptions);
308 if(!This->reported_result)
311 IInternetProtocol_Terminate(This->protocol, 0);
313 set_binding_sink(PROTOCOL(This), NULL);
315 if(This->bind_info) {
316 IInternetBindInfo_Release(This->bind_info);
317 This->bind_info = NULL;
323 static HRESULT WINAPI BindProtocol_Suspend(IInternetProtocol *iface)
325 BindProtocol *This = PROTOCOL_THIS(iface);
326 FIXME("(%p)\n", This);
330 static HRESULT WINAPI BindProtocol_Resume(IInternetProtocol *iface)
332 BindProtocol *This = PROTOCOL_THIS(iface);
333 FIXME("(%p)\n", This);
337 static HRESULT WINAPI BindProtocol_Read(IInternetProtocol *iface, void *pv,
338 ULONG cb, ULONG *pcbRead)
340 BindProtocol *This = PROTOCOL_THIS(iface);
344 TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
346 hres = IInternetProtocol_Read(This->protocol, pv, cb, &read);
352 static HRESULT WINAPI BindProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
353 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
355 BindProtocol *This = PROTOCOL_THIS(iface);
356 FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
360 static HRESULT WINAPI BindProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
362 BindProtocol *This = PROTOCOL_THIS(iface);
364 TRACE("(%p)->(%08x)\n", This, dwOptions);
366 return IInternetProtocol_LockRequest(This->protocol, dwOptions);
369 static HRESULT WINAPI BindProtocol_UnlockRequest(IInternetProtocol *iface)
371 BindProtocol *This = PROTOCOL_THIS(iface);
373 TRACE("(%p)\n", This);
375 return IInternetProtocol_UnlockRequest(This->protocol);
378 void set_binding_sink(IInternetProtocol *bind_protocol, IInternetProtocolSink *sink)
380 BindProtocol *This = PROTOCOL_THIS(bind_protocol);
381 IInternetProtocolSink *prev_sink;
382 IServiceProvider *service_provider = NULL;
385 IInternetProtocolSink_AddRef(sink);
386 prev_sink = InterlockedExchangePointer((void**)&This->protocol_sink, sink);
388 IInternetProtocolSink_Release(prev_sink);
391 IInternetProtocolSink_QueryInterface(sink, &IID_IServiceProvider, (void**)&service_provider);
392 service_provider = InterlockedExchangePointer((void**)&This->service_provider, service_provider);
394 IServiceProvider_Release(service_provider);
397 IWinInetInfo *get_wininet_info(IInternetProtocol *bind_protocol)
399 BindProtocol *This = PROTOCOL_THIS(bind_protocol);
401 return This->wininet_info;
406 static const IInternetProtocolVtbl BindProtocolVtbl = {
407 BindProtocol_QueryInterface,
409 BindProtocol_Release,
411 BindProtocol_Continue,
413 BindProtocol_Terminate,
414 BindProtocol_Suspend,
418 BindProtocol_LockRequest,
419 BindProtocol_UnlockRequest
422 #define BINDINFO_THIS(iface) DEFINE_THIS(BindProtocol, InternetBindInfo, iface)
424 static HRESULT WINAPI BindInfo_QueryInterface(IInternetBindInfo *iface,
425 REFIID riid, void **ppv)
427 BindProtocol *This = BINDINFO_THIS(iface);
428 return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
431 static ULONG WINAPI BindInfo_AddRef(IInternetBindInfo *iface)
433 BindProtocol *This = BINDINFO_THIS(iface);
434 return IBinding_AddRef(PROTOCOL(This));
437 static ULONG WINAPI BindInfo_Release(IInternetBindInfo *iface)
439 BindProtocol *This = BINDINFO_THIS(iface);
440 return IBinding_Release(PROTOCOL(This));
443 static HRESULT WINAPI BindInfo_GetBindInfo(IInternetBindInfo *iface,
444 DWORD *grfBINDF, BINDINFO *pbindinfo)
446 BindProtocol *This = BINDINFO_THIS(iface);
449 TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
451 hres = IInternetBindInfo_GetBindInfo(This->bind_info, grfBINDF, pbindinfo);
453 WARN("GetBindInfo failed: %08x\n", hres);
457 *grfBINDF |= BINDF_FROMURLMON;
461 static HRESULT WINAPI BindInfo_GetBindString(IInternetBindInfo *iface,
462 ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
464 BindProtocol *This = BINDINFO_THIS(iface);
466 TRACE("(%p)->(%d %p %d %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
468 return IInternetBindInfo_GetBindString(This->bind_info, ulStringType, ppwzStr, cEl, pcElFetched);
473 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
474 BindInfo_QueryInterface,
477 BindInfo_GetBindInfo,
478 BindInfo_GetBindString
481 #define PRIORITY_THIS(iface) DEFINE_THIS(BindProtocol, InternetPriority, iface)
483 static HRESULT WINAPI InternetPriority_QueryInterface(IInternetPriority *iface,
484 REFIID riid, void **ppv)
486 BindProtocol *This = PRIORITY_THIS(iface);
487 return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
490 static ULONG WINAPI InternetPriority_AddRef(IInternetPriority *iface)
492 BindProtocol *This = PRIORITY_THIS(iface);
493 return IInternetProtocol_AddRef(PROTOCOL(This));
496 static ULONG WINAPI InternetPriority_Release(IInternetPriority *iface)
498 BindProtocol *This = PRIORITY_THIS(iface);
499 return IInternetProtocol_Release(PROTOCOL(This));
502 static HRESULT WINAPI InternetPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
504 BindProtocol *This = PRIORITY_THIS(iface);
506 TRACE("(%p)->(%d)\n", This, nPriority);
508 This->priority = nPriority;
512 static HRESULT WINAPI InternetPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
514 BindProtocol *This = PRIORITY_THIS(iface);
516 TRACE("(%p)->(%p)\n", This, pnPriority);
518 *pnPriority = This->priority;
524 static const IInternetPriorityVtbl InternetPriorityVtbl = {
525 InternetPriority_QueryInterface,
526 InternetPriority_AddRef,
527 InternetPriority_Release,
528 InternetPriority_SetPriority,
529 InternetPriority_GetPriority
533 #define PROTSINK_THIS(iface) DEFINE_THIS(BindProtocol, InternetProtocolSink, iface)
535 static HRESULT WINAPI BPInternetProtocolSink_QueryInterface(IInternetProtocolSink *iface,
536 REFIID riid, void **ppv)
538 BindProtocol *This = PROTSINK_THIS(iface);
539 return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
542 static ULONG WINAPI BPInternetProtocolSink_AddRef(IInternetProtocolSink *iface)
544 BindProtocol *This = PROTSINK_THIS(iface);
545 return IInternetProtocol_AddRef(PROTOCOL(This));
548 static ULONG WINAPI BPInternetProtocolSink_Release(IInternetProtocolSink *iface)
550 BindProtocol *This = PROTSINK_THIS(iface);
551 return IInternetProtocol_Release(PROTOCOL(This));
555 task_header_t header;
559 static void switch_proc(BindProtocol *bind, task_header_t *t)
561 switch_task_t *task = (switch_task_t*)t;
563 IInternetProtocol_Continue(bind->protocol, &task->data);
568 static HRESULT WINAPI BPInternetProtocolSink_Switch(IInternetProtocolSink *iface,
569 PROTOCOLDATA *pProtocolData)
571 BindProtocol *This = PROTSINK_THIS(iface);
573 TRACE("(%p)->(%p)\n", This, pProtocolData);
575 TRACE("flags %x state %x data %p cb %u\n", pProtocolData->grfFlags, pProtocolData->dwState,
576 pProtocolData->pData, pProtocolData->cbData);
578 if(!do_direct_notif(This)) {
581 task = heap_alloc(sizeof(switch_task_t));
583 return E_OUTOFMEMORY;
585 task->data = *pProtocolData;
587 push_task(This, &task->header, switch_proc);
591 if(!This->protocol_sink) {
592 IInternetProtocol_Continue(This->protocol, pProtocolData);
596 return IInternetProtocolSink_Switch(This->protocol_sink, pProtocolData);
599 static HRESULT WINAPI BPInternetProtocolSink_ReportProgress(IInternetProtocolSink *iface,
600 ULONG ulStatusCode, LPCWSTR szStatusText)
602 BindProtocol *This = PROTSINK_THIS(iface);
604 TRACE("(%p)->(%u %s)\n", This, ulStatusCode, debugstr_w(szStatusText));
606 switch(ulStatusCode) {
607 case BINDSTATUS_FINDINGRESOURCE:
608 case BINDSTATUS_CONNECTING:
609 case BINDSTATUS_BEGINDOWNLOADDATA:
610 case BINDSTATUS_SENDINGREQUEST:
611 case BINDSTATUS_CACHEFILENAMEAVAILABLE:
612 case BINDSTATUS_DIRECTBIND:
613 case BINDSTATUS_ACCEPTRANGES:
614 case BINDSTATUS_MIMETYPEAVAILABLE:
615 if(!This->protocol_sink)
617 return IInternetProtocolSink_ReportProgress(This->protocol_sink,
618 ulStatusCode, szStatusText);
620 case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE:
621 if(!This->protocol_sink)
623 return IInternetProtocolSink_ReportProgress(This->protocol_sink,
624 This->from_urlmon ? BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : BINDSTATUS_MIMETYPEAVAILABLE,
627 FIXME("unsupported ulStatusCode %u\n", ulStatusCode);
633 static HRESULT WINAPI BPInternetProtocolSink_ReportData(IInternetProtocolSink *iface,
634 DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)
636 BindProtocol *This = PROTSINK_THIS(iface);
638 TRACE("(%p)->(%d %u %u)\n", This, grfBSCF, ulProgress, ulProgressMax);
640 if(!This->protocol_sink)
643 return IInternetProtocolSink_ReportData(This->protocol_sink, grfBSCF, ulProgress, ulProgressMax);
646 static HRESULT WINAPI BPInternetProtocolSink_ReportResult(IInternetProtocolSink *iface,
647 HRESULT hrResult, DWORD dwError, LPCWSTR szResult)
649 BindProtocol *This = PROTSINK_THIS(iface);
651 TRACE("(%p)->(%08x %d %s)\n", This, hrResult, dwError, debugstr_w(szResult));
653 if(!This->protocol_sink)
656 This->reported_result = TRUE;
658 return IInternetProtocolSink_ReportResult(This->protocol_sink, hrResult, dwError, szResult);
663 static const IInternetProtocolSinkVtbl InternetProtocolSinkVtbl = {
664 BPInternetProtocolSink_QueryInterface,
665 BPInternetProtocolSink_AddRef,
666 BPInternetProtocolSink_Release,
667 BPInternetProtocolSink_Switch,
668 BPInternetProtocolSink_ReportProgress,
669 BPInternetProtocolSink_ReportData,
670 BPInternetProtocolSink_ReportResult
673 #define SERVPROV_THIS(iface) DEFINE_THIS(BindProtocol, ServiceProvider, iface)
675 static HRESULT WINAPI BPServiceProvider_QueryInterface(IServiceProvider *iface,
676 REFIID riid, void **ppv)
678 BindProtocol *This = SERVPROV_THIS(iface);
679 return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
682 static ULONG WINAPI BPServiceProvider_AddRef(IServiceProvider *iface)
684 BindProtocol *This = SERVPROV_THIS(iface);
685 return IInternetProtocol_AddRef(PROTOCOL(This));
688 static ULONG WINAPI BPServiceProvider_Release(IServiceProvider *iface)
690 BindProtocol *This = SERVPROV_THIS(iface);
691 return IInternetProtocol_Release(PROTOCOL(This));
694 static HRESULT WINAPI BPServiceProvider_QueryService(IServiceProvider *iface,
695 REFGUID guidService, REFIID riid, void **ppv)
697 BindProtocol *This = SERVPROV_THIS(iface);
699 TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
701 if(!This->service_provider)
702 return E_NOINTERFACE;
704 return IServiceProvider_QueryService(This->service_provider, guidService, riid, ppv);
709 static const IServiceProviderVtbl ServiceProviderVtbl = {
710 BPServiceProvider_QueryInterface,
711 BPServiceProvider_AddRef,
712 BPServiceProvider_Release,
713 BPServiceProvider_QueryService
716 HRESULT create_binding_protocol(LPCWSTR url, BOOL from_urlmon, IInternetProtocol **protocol)
718 BindProtocol *ret = heap_alloc_zero(sizeof(BindProtocol));
720 ret->lpInternetProtocolVtbl = &BindProtocolVtbl;
721 ret->lpInternetBindInfoVtbl = &InternetBindInfoVtbl;
722 ret->lpInternetPriorityVtbl = &InternetPriorityVtbl;
723 ret->lpServiceProviderVtbl = &ServiceProviderVtbl;
724 ret->lpInternetProtocolSinkVtbl = &InternetProtocolSinkVtbl;
727 ret->from_urlmon = from_urlmon;
728 ret->apartment_thread = GetCurrentThreadId();
729 ret->notif_hwnd = get_notif_hwnd();
730 InitializeCriticalSection(&ret->section);
734 *protocol = PROTOCOL(ret);