mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
5d58931b51
While testing the unrelated pile of commits just past, I accidentally started a Cygwin saved session I hadn't run in ages which used the old Telnet-based cygtermd as a local proxy command, and found that it presented the Cygwin prompt with a trust sigil. Oops! It turns out that this is because interactor_return_seat does two things that can change the real seat's trust status, and it does them in the wrong order: it defaults the status back to trusted (as if the seat was brand new, because that's how they start out), and it calls tempseat_flush which may have buffered a trust-status reset while the seat was borrowed. The former should not override the latter!
120 lines
3.6 KiB
C
120 lines
3.6 KiB
C
/*
|
|
* Centralised functions for the Interactor trait.
|
|
*/
|
|
|
|
#include "putty.h"
|
|
|
|
Seat *interactor_borrow_seat(Interactor *itr)
|
|
{
|
|
Seat *clientseat = interactor_get_seat(itr);
|
|
if (!clientseat)
|
|
return NULL;
|
|
|
|
/* If the client has already had its Seat borrowed, then look
|
|
* through the existing TempSeat to find the underlying one. */
|
|
if (is_tempseat(clientseat))
|
|
return tempseat_get_real(clientseat);
|
|
|
|
/* Otherwise, make a new TempSeat and give that to the client. */
|
|
Seat *tempseat = tempseat_new(clientseat);
|
|
interactor_set_seat(itr, tempseat);
|
|
return clientseat;
|
|
}
|
|
|
|
static Interactor *interactor_toplevel(Interactor *itr, unsigned *level_out)
|
|
{
|
|
/*
|
|
* Find the Interactor at the top of the chain, so that all the
|
|
* Interactors in a stack can share that one's last-to-talk field.
|
|
* Also, count how far we had to go to get to it, to put in the
|
|
* message.
|
|
*/
|
|
Interactor *itr_top = itr;
|
|
unsigned level = 0;
|
|
while (itr_top->parent) {
|
|
itr_top = itr_top->parent;
|
|
level++;
|
|
}
|
|
|
|
if (level_out)
|
|
*level_out = level;
|
|
return itr_top;
|
|
}
|
|
|
|
void interactor_return_seat(Interactor *itr)
|
|
{
|
|
Seat *tempseat = interactor_get_seat(itr);
|
|
if (!is_tempseat(tempseat))
|
|
return; /* no-op */
|
|
|
|
/*
|
|
* We're about to hand this seat back to the parent Interactor to
|
|
* do its own thing with. It will typically expect to start in the
|
|
* same state as if the seat had never been borrowed, i.e. in the
|
|
* starting trust state.
|
|
*
|
|
* However, this may be overridden by the tempseat_flush call.
|
|
*/
|
|
Seat *realseat = tempseat_get_real(tempseat);
|
|
seat_set_trust_status(realseat, true);
|
|
|
|
tempseat_flush(tempseat);
|
|
interactor_set_seat(itr, realseat);
|
|
tempseat_free(tempseat);
|
|
|
|
/*
|
|
* If we have a parent Interactor, and anyone has ever called
|
|
* interactor_announce, then all Interactors from now on will
|
|
* announce themselves even if they have nothing to say.
|
|
*/
|
|
Interactor *itr_top = interactor_toplevel(itr, NULL);
|
|
if (itr_top->last_to_talk)
|
|
interactor_announce(itr);
|
|
}
|
|
|
|
InteractionReadySeat interactor_announce(Interactor *itr)
|
|
{
|
|
Seat *seat = interactor_get_seat(itr);
|
|
assert(!is_tempseat(seat) &&
|
|
"Shouldn't call announce when someone else is using our seat");
|
|
|
|
InteractionReadySeat iseat;
|
|
iseat.seat = seat;
|
|
|
|
unsigned level;
|
|
Interactor *itr_top = interactor_toplevel(itr, &level);
|
|
|
|
/*
|
|
* Generally, we should announce ourself if the previous
|
|
* Interactor that said anything was not us. That includes if
|
|
* there was no previous Interactor to talk (i.e. if we're the
|
|
* first to say anything) - *except* that the primary Interactor
|
|
* doesn't need to announce itself, if no proxy has intervened
|
|
* before it.
|
|
*/
|
|
bool need_announcement = (itr_top->last_to_talk != itr);
|
|
if (!itr->parent && !itr_top->last_to_talk)
|
|
need_announcement = false;
|
|
|
|
if (need_announcement) {
|
|
const char *prefix = "";
|
|
if (itr_top->last_to_talk != NULL)
|
|
seat_antispoof_msg(iseat, ""); /* leave a separating blank line */
|
|
|
|
char *desc = interactor_description(itr);
|
|
char *adjective = (level == 0 ? dupstr("primary") :
|
|
level == 1 ? dupstr("proxy") :
|
|
dupprintf("proxy^%u", level));
|
|
char *msg = dupprintf("%sMaking %s %s", prefix, adjective, desc);
|
|
sfree(adjective);
|
|
sfree(desc);
|
|
|
|
seat_antispoof_msg(iseat, msg);
|
|
sfree(msg);
|
|
|
|
itr_top->last_to_talk = itr;
|
|
}
|
|
|
|
return iseat;
|
|
}
|