UtlBoolean SipRefreshManager::initiateRefresh(SipMessage& subscribeOrRegisterRequest, void* applicationData, const RefreshStateCallback refreshStateCallback, UtlString& earlyDialogHandle) { UtlBoolean intitialRequestSent = FALSE; // Make sure we do not have an existing dialog or refresh session state // going for the given message UtlString messageDialogHandle; subscribeOrRegisterRequest.getDialogHandle(messageDialogHandle); UtlBoolean existingRefreshState = FALSE; UtlBoolean existingDialogState = FALSE; if(!SipDialog::isEarlyDialog(messageDialogHandle)) { existingDialogState = TRUE; OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::initiateRefresh called with established dialog handle: %s", messageDialogHandle.data()); } else { OsLock localLock(mRefreshMgrMutex); // See if there is an early or established dialog for this message if(getAnyDialog(messageDialogHandle)) { existingRefreshState = TRUE; intitialRequestSent = FALSE; OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::initiateRefresh called with pre-existing refresh state: %s", messageDialogHandle.data()); } // The dialog should not exist either else if(mpDialogMgr->dialogExists(messageDialogHandle) || mpDialogMgr->earlyDialogExistsFor(messageDialogHandle)) { existingDialogState = TRUE; OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::initiateRefresh called with pre-existing dialog: %s", messageDialogHandle.data()); } } // Should not be any existing refresh or dialog states // for this message if(!existingRefreshState && !existingDialogState) { // Make sure we are registered to receive responses // for the message we are about to send UtlString method; subscribeOrRegisterRequest.getRequestMethod(&method); if(method.compareTo(SIP_REGISTER_METHOD) == 0) { lock(); if(!mReceivingRegisterResponses) { mReceivingRegisterResponses = TRUE; // receive REGISTER responses mpUserAgent->addMessageObserver(*(getMessageQueue()), SIP_REGISTER_METHOD, FALSE, // yes requests TRUE, // no responses TRUE, // incoming, FALSE, // outgoing NULL); } unlock(); } else if(method.compareTo(SIP_SUBSCRIBE_METHOD) == 0) { UtlString eventType; subscribeOrRegisterRequest.getEventField(&eventType, NULL); // Check to see if we have already registered to // receive the event type lock(); if(mEventTypes.find(&eventType) == NULL) { mEventTypes.insert(new UtlString(eventType)); // receive SUBSCRIBE responses for this event type mpUserAgent->addMessageObserver(*(getMessageQueue()), SIP_SUBSCRIBE_METHOD, FALSE, // no requests TRUE, // yes responses TRUE, // incoming, FALSE, // outgoing eventType); } unlock(); } // Create a new refresh state int requestedExpiration = 0; // returned from following call RefreshDialogState* state = createNewRefreshState(subscribeOrRegisterRequest, messageDialogHandle, applicationData, refreshStateCallback, requestedExpiration); // create a new dialog mpDialogMgr->createDialog(subscribeOrRegisterRequest, TRUE, // message from this side messageDialogHandle); // Keep track of when we send this request to be refreshed long now = OsDateTime::getSecsSinceEpoch(); state->mPendingStartTime = now; state->mRequestState = REFRESH_REQUEST_PENDING; // Set a timer at which to resend the next refresh based upon the // assumption that the request will succeed. When we receive a // failed response, we will cancel the timer and reschedule // a new timer based upon a smaller fraction of the requested // expiration period setRefreshTimer(*state, TRUE); // Resend with successful timeout OsTimer* resendTimer = state->mpRefreshTimer; // Mark the refresh state as having an outstanding request // and make a copy of the request. The copy needs to be // attached to the state before the send incase the response // comes back before we return from the send. state->mRequestState = REFRESH_REQUEST_PENDING; state->mpLastRequest = new SipMessage(subscribeOrRegisterRequest); // Add the state to the container of refresh states // No need to lock this refreshMgr earlier as this is a new // state and no one can touch it until it is in the list. lock(); mRefreshes.insert(state); unlock(); // NOTE: at this point is is no longer safe to touch the state // without locking it again. Avoid locking this refresh mgr // when something can block (e.g. like calling SipUserAgent ::send) // Send the request // Is the correct? Should we send the request first and only set // a timer if the request succeeds?? intitialRequestSent = mpUserAgent->send(subscribeOrRegisterRequest); // We do not clean up the state even if the send fails. // The application must end the refresh as the refresh // manager should retry the send if it failed if(!intitialRequestSent) { // Need to lock this refresh mgr and make sure the state // still exists. It could have been deleted while this // was unlocked above. lock(); if(stateExists(state)) { // It is now safe to touch the state again // Mark the state of the last request as having // failed, so we know to resend when the timer // fires state->mRequestState = REFRESH_REQUEST_FAILED; // The expiration should still be set to zero // the initial send failed, cancel the timer and // fire the notification. The handleMessage method // will see that the subscription or registration // has never succeeded and reshedule the timer for // a failure timeout. We cannot reschedule the // timer here as there is a race condition between // deleting the timer here (in the applications context) // and in handleMessage in this refresh manager's // context. // If the timer has not changed assume it is still safe // to touch it if(state->mpRefreshTimer == resendTimer) { stopTimerForFailureReschedule(state->mpRefreshTimer); } // Do not notify the application that the request failed // when it occurs on the first invokation. The application // will know by the return. } unlock(); } } return(intitialRequestSent); }
UtlBoolean SipRefreshManager::handleMessage(OsMsg &eventMessage) { int msgType = eventMessage.getMsgType(); int msgSubType = eventMessage.getMsgSubType(); // Timer fired if(msgType == OsMsg::OS_EVENT && msgSubType == OsEventMsg::NOTIFY) { int eventData = 0; RefreshDialogState* state = NULL; ((OsEventMsg&)eventMessage).getUserData((int&)state); ((OsEventMsg&)eventMessage).getEventData(eventData); lock(); // If the state is not still in the list we cannot // touch it. It may have been deleted. if(state && stateExists(state)) { // Refresh request failed, need to clean up and // schedule a refresh in a short/failed time period if(eventData == OS_INTERRUPTED) { // Clean up the timer and notifier deleteTimerAndEvent(state->mpRefreshTimer); state->mpRefreshTimer = NULL; // Create and set a new timer for the failed time out period setRefreshTimer(*state, FALSE); // Resend with failure timeout } // Normal timer fire, time to refresh else if(eventData != 0 || ((OsTimer*)eventData) == state->mpRefreshTimer) { // Clean up the timer and notifier deleteTimerAndEvent(state->mpRefreshTimer); state->mpRefreshTimer = NULL; // Legitimate states to reSUBSCRIBE or reREGISTER if(state->mRequestState == REFRESH_REQUEST_FAILED || state->mRequestState == REFRESH_REQUEST_SUCCEEDED) { // Create and set a new timer for resending assuming // the resend is successful. If it fails we will // cancel the timer and set a shorter timeout setRefreshTimer(*state, TRUE); // Resend with successful timeout // reset the message for resend setForResend(*state, FALSE); // do not expire now // Keep track of when this refresh is sent so we know // when the new expiration is relative to. state->mPendingStartTime = OsDateTime::getSecsSinceEpoch(); // Do not want to keep the lock while we send the // message as it could block. Presumably it is better // to incure the cost of copying the message???? SipMessage tempRequest(*(state->mpLastRequest)); unlock(); mpUserAgent->send(tempRequest); // do not need the lock any more, but this gives us // clean locking symmetry. DO NOT TOUCH state or // any of its members BEYOND this point as it may // have been deleted lock(); } // This should not happen else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::handleMessage timer fired for state: %d", state->mRequestState); if(state->mRequestState == REFRESH_REQUEST_PENDING) { // Try again later if it was pending setRefreshTimer(*state, FALSE); // Resend with failed timeout } } } // Bad do not know what happened else { OsSysLog::add(FAC_SIP, PRI_ERR, "SipRefreshManager::handleMessage timer: %x does not match states timer: %p", eventData, state->mpRefreshTimer); } } unlock(); } // SIP message else if(msgType == OsMsg::PHONE_APP && msgSubType == SipMessage::NET_SIP_MESSAGE) { const SipMessage* sipMessage = ((SipMessageEvent&)eventMessage).getMessage(); int messageType = ((SipMessageEvent&)eventMessage).getMessageStatus(); // messageType can be: // SipMessageEvent::TRANSPORT_ERROR for requests that do not get sent // but we would have to register with the SipUserAgent to // receive requests. // SipMessageEvent::AUTHENTICATION_RETRY for 401 or 407 responses // that are resent with credentials. We ge this message so // that we can keep the dialog info. up to date. // SipMessageEvent::APPLICATION normal messages // For now we will treat the APPLICATION and AUTHENTICATION_RETRY // identically. // If this is a SUBSCRIBE or REGISTER response UtlString method; int cseq; if(sipMessage) sipMessage->getCSeqField(&cseq, &method); if(sipMessage && sipMessage->isResponse() && (method.compareTo(SIP_SUBSCRIBE_METHOD) == 0 || method.compareTo(SIP_REGISTER_METHOD) == 0)) { UtlString eventField; sipMessage->getEventField(&eventField, NULL); // We could validate that the event field is // set and is the right event type, but mostly // we should not care as we know the event type // from the subscription. We can be tolerant of // missing or malformed event headers in the // NOTIFY request. The event header is not // required in the subscribe response. UtlString dialogHandle; UtlString earlyDialogHandle; sipMessage->getDialogHandle(dialogHandle); UtlBoolean foundDialog = mpDialogMgr->dialogExists(dialogHandle); UtlBoolean foundEarlyDialog = FALSE; UtlBoolean matchesLastLocalTransaction = FALSE; if(foundDialog) { matchesLastLocalTransaction = mpDialogMgr->isLastLocalTransaction(*sipMessage, dialogHandle); } else { foundEarlyDialog = mpDialogMgr->getEarlyDialogHandleFor(dialogHandle, earlyDialogHandle); if(foundEarlyDialog) { matchesLastLocalTransaction = mpDialogMgr->isLastLocalTransaction(*sipMessage, earlyDialogHandle); } } #ifdef TEST_PRINT osPrintf("Looking for refresh state with dialog handle: %s\n", dialogHandle.data()); UtlString refreshStateDump; dumpRefreshStates(refreshStateDump); osPrintf("SipRefreshManager::handleMessage state dump:\n%s\n", refreshStateDump.data()); #endif lock(); // Find the refresh state for this response RefreshDialogState* state = NULL; if(foundDialog && matchesLastLocalTransaction) { state = (RefreshDialogState*) mRefreshes.find(&dialogHandle); // Check if the key has the tags reversed if(state == NULL) { UtlString reversedDialogHandle; SipDialog::reverseTags(dialogHandle, reversedDialogHandle); state = (RefreshDialogState*) mRefreshes.find(&reversedDialogHandle); } } else if(foundEarlyDialog && matchesLastLocalTransaction) { state = (RefreshDialogState*) mRefreshes.remove(&earlyDialogHandle); // See if the key has the tags reversed if(state == NULL) { UtlString reversedEarlyDialogHandle; SipDialog::reverseTags(earlyDialogHandle, reversedEarlyDialogHandle); state = (RefreshDialogState*) mRefreshes.remove(&reversedEarlyDialogHandle); } if(state) { #ifdef TEST_PRINT osPrintf("Removed refresh state with dialog handle: %s\n", state->data()); osPrintf("Inserting refresh state with dialog handle: %s\n", dialogHandle.data()); #endif // Fix the state handle and put it back in the list *((UtlString*) state) = dialogHandle; mRefreshes.insert(state); } } if(state) { // Need to check for error responses or 2xx class responses int responseCode = sipMessage->getResponseStatusCode(); UtlString responseText; sipMessage->getResponseStatusText(&responseText); // Update the expiration members int expirationPeriod = 0; if(responseCode >= SIP_2XX_CLASS_CODE && responseCode < SIP_3XX_CLASS_CODE) { // Should we tolerate no Expires header in response? // Currently assume that if Expires header is not // set that we got what we asked for if(!getAcceptedExpiration(state, *sipMessage, expirationPeriod)) { expirationPeriod = state->mExpirationPeriodSeconds; } // SUBSCRIBE or REGISTER gave us expiration seconds // from when the request was sent. if(expirationPeriod > 0) { state->mExpiration = state->mPendingStartTime + expirationPeriod; } // UnSUBSCRIBE or unREGISTER else { state->mExpiration = 0; } // The request succeeded state->mRequestState = REFRESH_REQUEST_SUCCEEDED; } // Provisional response, do nothing else if(responseCode < SIP_2XX_CLASS_CODE) { } // There was a resend with credentials for this // failed response (should be a 401 or 407 auth. // challenge. else if(messageType == SipMessageEvent::AUTHENTICATION_RETRY) { // Do not stop the timer and do not change the // state from PENDING to FAILED } // a non-success response code, don't care what // type of error. It is the applications job // to care. If the credentials are wrong, the // application needs to fix it. We will just keep // on trying, hoping that the credentials get fixed on //this side or on the server side else { state->mFailedResponseCode = responseCode; state->mFailedResponseText = responseText; state->mRequestState = REFRESH_REQUEST_FAILED; // Do not change the expiration date, it // is what ever it was before the response was // sent. // Kill the timer and have it fire now and // reschedule with the error timeout. We // cannot delete the timer or notifier here // as there is a race condition where the // timer might fire before we delete it. Then // it would be deleted twice (i.e. bad). stopTimerForFailureReschedule(state->mpRefreshTimer); } //updateState(state, sipMessage); mpDialogMgr->updateDialog(*sipMessage); // Invoke the callback to let the application // know that the state changed if(state->mpStateCallback) { (state->mpStateCallback)(state->mRequestState, earlyDialogHandle, dialogHandle, state->mpApplicationData, responseCode, // responseCode responseText, // responseText, state->mExpiration, // zero means expires now sipMessage); // response } } unlock(); } // endif SUBSCRIBE or REGISTER response } // endif SipMessage event return(TRUE); }
bool KstViewPicture::setImage(const QString& source) { KURL url; if (QFile::exists(source) && QFileInfo(source).isRelative()) { url.setPath(source); } else { url = KURL::fromPathOrURL(source); } #if KDE_VERSION >= KDE_MAKE_VERSION(3,3,0) // FIXME: come up with a way to indicate the "widget" and fill it in here so // that KIO dialogs are associated with the proper window if (!KIO::NetAccess::exists(url, true, 0L)) { #else if (!KIO::NetAccess::exists(url, true)) { #endif return false; } QString tmpFile; #if KDE_VERSION >= KDE_MAKE_VERSION(3,3,0) // FIXME: come up with a way to indicate the "widget" and fill it in here so // that KIO dialogs are associated with the proper window if (!KIO::NetAccess::download(url, tmpFile, 0L)) { #else if (!KIO::NetAccess::download(url, tmpFile)) { #endif return false; } bool success = true; QImage ti; ti.setAlphaBuffer(true); if (ti.load(tmpFile)) { setImage(ti); _url = source; if (_maintainAspect == true) { restoreAspect(); } } else { success = false; } KIO::NetAccess::removeTempFile(tmpFile); return success; } void KstViewPicture::setImage(const QImage& image) { _url = QString::null; _image = image; _iCache = QImage(); setDirty(); } const QString& KstViewPicture::url() const { return _url; } const QImage& KstViewPicture::image() const { return _image; } void KstViewPicture::doRefresh() { if (_url.isEmpty()) { setRefreshTimer(0); } else { QString u = _url; bool rc = setImage(_url); _url = u; // FIXME: after n failures, give up? if (rc) { KstApp::inst()->paintAll(KstPainter::P_PAINT); } } } void KstViewPicture::setRefreshTimer(int seconds) { _refresh = kMax(0, seconds); if (_refresh) { if (!_timer) { _timer = new QTimer(this); connect(_timer, SIGNAL(timeout()), this, SLOT(doRefresh())); } _timer->start(_refresh * 1000, false); } else { delete _timer; _timer = 0L; } }